/*
 ===========================================================================
 Copyright (C) 1999-2005 Id Software, Inc.
 Copyright (C) 2000-2006 Tim Angus

 This file is part of Tremulous.

 Tremulous is free software; you can redistribute it
 and/or modify it under the terms of the GNU General Public License as
 published by the Free Software Foundation; either version 2 of the License,
 or (at your option) any later version.

 Tremulous is distributed in the hope that it will be
 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with Tremulous; if not, write to the Free Software
 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 ===========================================================================
 */

// bg_misc.c -- both games misc functions, all completely stateless

#include "../qcommon/q_shared.h"
#include "bg_public.h"

int
trap_FS_FOpenFile(const char *qpath, fileHandle_t *f, fsMode_t mode);
void
trap_FS_Read(void *buffer, int len, fileHandle_t f);
void
trap_FS_Write(const void *buffer, int len, fileHandle_t f);
void
trap_FS_FCloseFile(fileHandle_t f);
void
trap_FS_Seek(fileHandle_t f, long offset, fsOrigin_t origin); // fsOrigin_t

buildableAttributes_t bg_buildableList[] =
{
{ BA_A_SPAWN, //int       buildNum;
    "eggpod", //char      *buildName;
    "Egg", //char      *humanName;
    "team_alien_spawn", //char      *entityName;
    { "models/buildables/telenode/telenode.md3", 0, 0, 0 }, 1.0f, //float     modelScale;
    { -40, -40, -4 }, //vec3_t    mins;
    { 40, 40, 4 }, //vec3_t    maxs;
    0.0f, //float     zOffset;
    TR_GRAVITY, //trType_t  traj;
    0.0, //float     bounce;
    HSPAWN_BP, //int       buildPoints;
    (1 << S1) | (1 << S2) | (1 << S3), //int  stages
    HSPAWN_HEALTH, //int       health;
    0, //int       regenRate;
    HSPAWN_SPLASHDAMAGE, //int       splashDamage;
    HSPAWN_SPLASHRADIUS, //int       splashRadius;
    MOD_HSPAWN, //int       meansOfDeath;
    BIT_HUMANS, //int       team;
    WP_NONE, //weapon_t  buildWeapon;
    BANIM_IDLE1, //int       idleAnim;
    100, //int       nextthink;
    HSPAWN_BT, //int       buildTime;
    qfalse, //qboolean  usable;
    0, //int       turretRange;
    0, //int       turretFireSpeed;
    WP_NONE, //weapon_t  turretProjType;
    0.95f, //float     minNormal;
    qfalse, //qboolean  invertNormal;
    qfalse, //qboolean  creepTest;
    0, //int       creepSize;
    qfalse, //qboolean  dccTest;
    qtrue, //qboolean  transparentTest;
    qfalse, //qboolean  reactorTest;
    qfalse, //qboolean  replacable;
    },
    { BA_A_BARRICADE, //int       buildNum;
        "barricade", //char      *buildName;
        "Barricade", //char      *humanName;
        "team_alien_barricade", //char      *entityName;
        { "models/buildables/barricade/barricade.md3", 0, 0, 0 }, 1.0f, //float     modelScale;
        { -35, -35, -15 }, //vec3_t    mins;
        { 35, 35, 60 }, //vec3_t    maxs;
        0.0f, //float     zOffset;
        TR_GRAVITY, //trType_t  traj;
        0.0, //float     bounce;
        BARRICADE_BP, //int       buildPoints;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        BARRICADE_HEALTH, //int       health;
        BARRICADE_REGEN, //int       regenRate;
        BARRICADE_SPLASHDAMAGE, //int       splashDamage;
        BARRICADE_SPLASHRADIUS, //int       splashRadius;
        MOD_ASPAWN, //int       meansOfDeath;
        BIT_ALIENS, //int       team;
        (1 << WP_ABUILD) | (1 << WP_ABUILD2), //weapon_t  buildWeapon;
        BANIM_IDLE1, //int       idleAnim;
        100, //int       nextthink;
        BARRICADE_BT, //int       buildTime;
        qfalse, //qboolean  usable;
        0, //int       turretRange;
        0, //int       turretFireSpeed;
        WP_NONE, //weapon_t  turretProjType;
        0.707f, //float     minNormal;
        qfalse, //qboolean  invertNormal;
        qtrue, //qboolean  creepTest;
        BARRICADE_CREEPSIZE, //int       creepSize;
        qfalse, //qboolean  dccTest;
        qfalse, //qboolean  transparentTest;
        qfalse, //qboolean  reactorTest;
        qfalse, //qboolean  replaceable;
    },
    { BA_A_BOOSTER, //int       buildNum;
        "booster", //char      *buildName;
        "Booster", //char      *humanName;
        "team_alien_booster", //char      *entityName;
        { "models/buildables/booster/booster.md3", 0, 0, 0 }, 1.0f, //float     modelScale;
        { -26, -26, -9 }, //vec3_t     mins;
        { 26, 26, 9 }, //vec3_t     maxs;
        0.0f, //float     zOffset;
        TR_GRAVITY, //trType_t  traj;
        0.0, //float     bounce;
        BOOSTER_BP, //int       buildPoints;
        (1 << S2) | (1 << S3), //int  stages
        BOOSTER_HEALTH, //int       health;
        BOOSTER_REGEN, //int       regenRate;
        BOOSTER_SPLASHDAMAGE, //int       splashDamage;
        BOOSTER_SPLASHRADIUS, //int       splashRadius;
        MOD_ASPAWN, //int       meansOfDeath;
        BIT_ALIENS, //int       team;
        (1 << WP_ABUILD) | (1 << WP_ABUILD2), //weapon_t  buildWeapon;
        BANIM_IDLE1, //int       idleAnim;
        100, //int       nextthink;
        BOOSTER_BT, //int       buildTime;
        qfalse, //qboolean  usable;
        0, //int       turretRange;
        0, //int       turretFireSpeed;
        WP_NONE, //weapon_t  turretProjType;
        0.707f, //float     minNormal;
        qfalse, //qboolean  invertNormal;
        qtrue, //qboolean  creepTest;
        BOOSTER_CREEPSIZE, //int       creepSize;
        qfalse, //qboolean  dccTest;
        qtrue, //qboolean  transparentTest;
        qfalse, //qboolean  reactorTest;
        qtrue, //qboolean  replacable;
    },
    { BA_A_ACIDTUBE, //int       buildNum;
        "acid_tube", //char      *buildName;
        "Acid Tube", //char      *humanName;
        "team_alien_acid_tube", //char      *entityName;
        { "models/buildables/acid_tube/acid_tube.md3", 0, 0, 0 }, 1.0f, //float     modelScale;
        { -25, -25, -25 }, //vec3_t    mins;
        { 25, 25, 25 }, //vec3_t    maxs;
        -15.0f, //float     zOffset;
        TR_GRAVITY, //trType_t  traj;
        0.0, //float     bounce;
        ACIDTUBE_BP, //int       buildPoints;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        ACIDTUBE_HEALTH, //int       health;
        ACIDTUBE_REGEN, //int       regenRate;
        ACIDTUBE_SPLASHDAMAGE, //int       splashDamage;
        ACIDTUBE_SPLASHRADIUS, //int       splashRadius;
        MOD_ATUBE, //int       meansOfDeath;
        BIT_ALIENS, //int       team;
        (1 << WP_ABUILD) | (1 << WP_ABUILD2), //weapon_t  buildWeapon;
        BANIM_IDLE1, //int       idleAnim;
        200, //int       nextthink;
        ACIDTUBE_BT, //int       buildTime;
        qfalse, //qboolean  usable;
        0, //int       turretRange;
        0, //int       turretFireSpeed;
        WP_NONE, //weapon_t  turretProjType;
        0.0f, //float     minNormal;
        qtrue, //qboolean  invertNormal;
        qtrue, //qboolean  creepTest;
        ACIDTUBE_CREEPSIZE, //int       creepSize;
        qfalse, //qboolean  dccTest;
        qfalse, //qboolean  transparentTest;
        qfalse, //qboolean  reactorTest;
        qfalse, //qboolean  replacable;
    },
    { BA_A_HIVE, //int       buildNum;
        "hive", //char      *buildName;
        "Hive", //char      *humanName;
        "team_alien_hive", //char      *entityName;
        { "models/buildables/acid_tube/acid_tube.md3", 0, 0, 0 }, 1.0f, //float     modelScale;
        { -35, -35, -25 }, //vec3_t    mins;
        { 35, 35, 25 }, //vec3_t    maxs;
        -15.0f, //float     zOffset;
        TR_GRAVITY, //trType_t  traj;
        0.0, //float     bounce;
        HIVE_BP, //int       buildPoints;
        (1 << S3), //int  stages
        HIVE_HEALTH, //int       health;
        HIVE_REGEN, //int       regenRate;
        HIVE_SPLASHDAMAGE, //int       splashDamage;
        HIVE_SPLASHRADIUS, //int       splashRadius;
        MOD_ASPAWN, //int       meansOfDeath;
        BIT_ALIENS, //int       team;
        (1 << WP_ABUILD) | (1 << WP_ABUILD2), //weapon_t  buildWeapon;
        BANIM_IDLE1, //int       idleAnim;
        500, //int       nextthink;
        HIVE_BT, //int       buildTime;
        qfalse, //qboolean  usable;
        0, //int       turretRange;
        0, //int       turretFireSpeed;
        WP_HIVE, //weapon_t  turretProjType;
        0.0f, //float     minNormal;
        qtrue, //qboolean  invertNormal;
        qtrue, //qboolean  creepTest;
        HIVE_CREEPSIZE, //int       creepSize;
        qfalse, //qboolean  dccTest;
        qfalse, //qboolean  transparentTest;
        qfalse, //qboolean  reactorTest;
        qfalse, //qboolean  replacable;
    },
    { BA_A_TRAPPER, //int       buildNum;
        "trapper", //char      *buildName;
        "Trapper", //char      *humanName;
        "team_alien_trapper", //char      *entityName;
        { "models/buildables/trapper/trapper.md3", 0, 0, 0 }, 1.0f, //float     modelScale;
        { -15, -15, -15 }, //vec3_t    mins;
        { 15, 15, 15 }, //vec3_t    maxs;
        0.0f, //float     zOffset;
        TR_GRAVITY, //trType_t  traj;
        0.0, //float     bounce;
        TRAPPER_BP, //int       buildPoints;
        (1 << S2) | (1 << S3), //int  stages //NEEDS ADV BUILDER SO S2 AND UP
        TRAPPER_HEALTH, //int       health;
        TRAPPER_REGEN, //int       regenRate;
        TRAPPER_SPLASHDAMAGE, //int       splashDamage;
        TRAPPER_SPLASHRADIUS, //int       splashRadius;
        MOD_ASPAWN, //int       meansOfDeath;
        BIT_ALIENS, //int       team;
        (1 << WP_ABUILD) | (1 << WP_ABUILD2), //weapon_t  buildWeapon;
        BANIM_IDLE1, //int       idleAnim;
        100, //int       nextthink;
        TRAPPER_BT, //int       buildTime;
        qfalse, //qboolean  usable;
        TRAPPER_RANGE, //int       turretRange;
        TRAPPER_REPEAT, //int       turretFireSpeed;
        WP_LOCKBLOB_LAUNCHER, //weapon_t  turretProjType;
        0.0f, //float     minNormal;
        qtrue, //qboolean  invertNormal;
        qtrue, //qboolean  creepTest;
        TRAPPER_CREEPSIZE, //int       creepSize;
        qfalse, //qboolean  dccTest;
        qtrue, //qboolean  transparentTest;
        qfalse, //qboolean  reactorTest;
        qfalse, //qboolean  replacable;
    },
    { BA_A_OVERMIND, //int       buildNum;
        "overmind", //char      *buildName;
        "Overmind", //char      *humanName;
        "team_alien_overmind", //char      *entityName;
        { "models/buildables/overmind/overmind.md3", 0, 0, 0 }, 1.0f, //float     modelScale;
        { -45, -45, -15 }, //vec3_t    mins;
        { 45, 45, 95 }, //vec3_t    maxs;
        0.0f, //float     zOffset;
        TR_GRAVITY, //trType_t  traj;
        0.0, //float     bounce;
        OVERMIND_BP, //int       buildPoints;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        OVERMIND_HEALTH, //int       health;
        OVERMIND_REGEN, //int       regenRate;
        OVERMIND_SPLASHDAMAGE, //int       splashDamage;
        OVERMIND_SPLASHRADIUS, //int       splashRadius;
        MOD_ASPAWN, //int       meansOfDeath;
        BIT_ALIENS, //int       team;
        (1 << WP_ABUILD) | (1 << WP_ABUILD2), //weapon_t  buildWeapon;
        BANIM_IDLE1, //int       idleAnim;
        OVERMIND_ATTACK_REPEAT, //int       nextthink;
        OVERMIND_BT, //int       buildTime;
        qfalse, //qboolean  usable;
        0, //int       turretRange;
        0, //int       turretFireSpeed;
        WP_NONE, //weapon_t  turretProjType;
        0.95f, //float     minNormal;
        qfalse, //qboolean  invertNormal;
        qfalse, //qboolean  creepTest;
        OVERMIND_CREEPSIZE, //int       creepSize;
        qfalse, //qboolean  dccTest;
        qfalse, //qboolean  transparentTest;
        qtrue, //qboolean  reactorTest;
        qtrue, //qboolean  replacable;
    },
    { BA_A_HOVEL, //int       buildNum;
        "hovel", //char      *buildName;
        "Hovel", //char      *humanName;
        "team_alien_hovel", //char      *entityName;
        { "models/buildables/hovel/hovel.md3", 0, 0, 0 }, 1.0f, //float     modelScale;
        { -50, -50, -20 }, //vec3_t    mins;
        { 50, 50, 20 }, //vec3_t    maxs;
        0.0f, //float     zOffset;
        TR_GRAVITY, //trType_t  traj;
        0.0, //float     bounce;
        HOVEL_BP, //int       buildPoints;
        (1 << S3), //int  stages
        HOVEL_HEALTH, //int       health;
        HOVEL_REGEN, //int       regenRate;
        HOVEL_SPLASHDAMAGE, //int       splashDamage;
        HOVEL_SPLASHRADIUS, //int       splashRadius;
        MOD_ASPAWN, //int       meansOfDeath;
        BIT_ALIENS, //int       team;
        (1 << WP_ABUILD) | (1 << WP_ABUILD2), //weapon_t  buildWeapon;
        BANIM_IDLE1, //int       idleAnim;
        150, //int       nextthink;
        HOVEL_BT, //int       buildTime;
        qtrue, //qboolean  usable;
        0, //int       turretRange;
        0, //int       turretFireSpeed;
        WP_NONE, //weapon_t  turretProjType;
        0.95f, //float     minNormal;
        qfalse, //qboolean  invertNormal;
        qtrue, //qboolean  creepTest;
        HOVEL_CREEPSIZE, //int       creepSize;
        qfalse, //qboolean  dccTest;
        qfalse, //qboolean  transparentTest;
        qtrue, //qboolean  reactorTest;
        qfalse, //qboolean  replacable;
    },
    { BA_H_SPAWN, //int       buildNum;
        "telenode", //char      *buildName;
        "Telenode", //char      *humanName;
        "team_human_spawn", //char      *entityName;
        { "models/buildables/telenode/telenode.md3", 0, 0, 0 }, 1.0f, //float     modelScale;
        { -40, -40, -4 }, //vec3_t    mins;
        { 40, 40, 4 }, //vec3_t    maxs;
        0.0f, //float     zOffset;
        TR_GRAVITY, //trType_t  traj;
        0.0, //float     bounce;
        HSPAWN_BP, //int       buildPoints;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        HSPAWN_HEALTH, //int       health;
        0, //int       regenRate;
        HSPAWN_SPLASHDAMAGE, //int       splashDamage;
        HSPAWN_SPLASHRADIUS, //int       splashRadius;
        MOD_HSPAWN, //int       meansOfDeath;
        BIT_HUMANS, //int       team;
        (1 << WP_HBUILD) | (1 << WP_HBUILD2), //weapon_t  buildWeapon;
        BANIM_IDLE1, //int       idleAnim;
        100, //int       nextthink;
        HSPAWN_BT, //int       buildTime;
        qfalse, //qboolean  usable;
        0, //int       turretRange;
        0, //int       turretFireSpeed;
        WP_NONE, //weapon_t  turretProjType;
        0.95f, //float     minNormal;
        qfalse, //qboolean  invertNormal;
        qfalse, //qboolean  creepTest;
        0, //int       creepSize;
        qfalse, //qboolean  dccTest;
        qtrue, //qboolean  transparentTest;
        qfalse, //qboolean  reactorTest;
        qfalse, //qboolean  replacable;
    },
    { BA_H_MEDISTAT, //int       buildNum;
        "medistat", //char      *buildName;
        "Medistation", //char      *humanName;
        "team_human_medistat", //char      *entityName;
        { "models/buildables/medistat/medistat.md3", 0, 0, 0 }, 1.0f, //float     modelScale;
        { -35, -35, -7 }, //vec3_t    mins;
        { 35, 35, 7 }, //vec3_t    maxs;
        0.0f, //float     zOffset;
        TR_GRAVITY, //trType_t  traj;
        0.0, //float     bounce;
        MEDISTAT_BP, //int       buildPoints;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        MEDISTAT_HEALTH, //int       health;
        0, //int       regenRate;
        MEDISTAT_SPLASHDAMAGE, //int       splashDamage;
        MEDISTAT_SPLASHRADIUS, //int       splashRadius;
        MOD_HSPAWN, //int       meansOfDeath;
        BIT_HUMANS, //int       team;
        (1 << WP_HBUILD) | (1 << WP_HBUILD2), //weapon_t  buildWeapon;
        BANIM_IDLE1, //int       idleAnim;
        100, //int       nextthink;
        MEDISTAT_BT, //int       buildTime;
        qfalse, //qboolean  usable;
        0, //int       turretRange;
        0, //int       turretFireSpeed;
        WP_NONE, //weapon_t  turretProjType;
        0.95f, //float     minNormal;
        qfalse, //qboolean  invertNormal;
        qfalse, //qboolean  creepTest;
        0, //int       creepSize;
        qfalse, //qboolean  dccTest;
        qtrue, //qboolean  transparentTest;
        qfalse, //qboolean  reactorTest;
        qtrue, //qboolean  replacable;
    },
    { BA_H_MGTURRET, //int       buildNum;
        "mgturret", //char      *buildName;
        "Machinegun Turret", //char      *humanName;
        "team_human_mgturret", //char      *entityName;
        { "models/buildables/mgturret/turret_base.md3",
            "models/buildables/mgturret/turret_barrel.md3",
            "models/buildables/mgturret/turret_top.md3", 0 }, 1.0f, //float     modelScale;
        { -25, -25, -20 }, //vec3_t    mins;
        { 25, 25, 20 }, //vec3_t    maxs;
        0.0f, //float     zOffset;
        TR_GRAVITY, //trType_t  traj;
        0.0, //float     bounce;
        MGTURRET_BP, //int       buildPoints;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        MGTURRET_HEALTH, //int       health;
        0, //int       regenRate;
        MGTURRET_SPLASHDAMAGE, //int       splashDamage;
        MGTURRET_SPLASHRADIUS, //int       splashRadius;
        MOD_HSPAWN, //int       meansOfDeath;
        BIT_HUMANS, //int       team;
        (1 << WP_HBUILD) | (1 << WP_HBUILD2), //weapon_t  buildWeapon;
        BANIM_IDLE1, //int       idleAnim;
        50, //int       nextthink;
        MGTURRET_BT, //int       buildTime;
        qfalse, //qboolean  usable;
        MGTURRET_RANGE, //int       turretRange;
        MGTURRET_REPEAT, //int       turretFireSpeed;
        WP_MGTURRET, //weapon_t  turretProjType;
        0.95f, //float     minNormal;
        qfalse, //qboolean  invertNormal;
        qfalse, //qboolean  creepTest;
        0, //int       creepSize;
        qfalse, //qboolean  dccTest;
        qtrue, //qboolean  transparentTest;
        qfalse, //qboolean  reactorTest;
        qfalse, //qboolean  replacable;
    },
    { BA_H_TESLAGEN, //int       buildNum;
        "tesla", //char      *buildName;
        "Tesla Generator", //char      *humanName;
        "team_human_tesla", //char      *entityName;
        { "models/buildables/tesla/tesla.md3", 0, 0, 0 }, 1.0f, //float     modelScale;
        { -22, -22, -40 }, //vec3_t    mins;
        { 22, 22, 40 }, //vec3_t    maxs;
        0.0f, //float     zOffset;
        TR_GRAVITY, //trType_t  traj;
        0.0, //float     bounce;
        TESLAGEN_BP, //int       buildPoints;
        (1 << S3), //int       stages
        TESLAGEN_HEALTH, //int       health;
        0, //int       regenRate;
        TESLAGEN_SPLASHDAMAGE, //int       splashDamage;
        TESLAGEN_SPLASHRADIUS, //int       splashRadius;
        MOD_HSPAWN, //int       meansOfDeath;
        BIT_HUMANS, //int       team;
        (1 << WP_HBUILD2), //weapon_t  buildWeapon;
        BANIM_IDLE1, //int       idleAnim;
        150, //int       nextthink;
        TESLAGEN_BT, //int       buildTime;
        qfalse, //qboolean  usable;
        TESLAGEN_RANGE, //int       turretRange;
        TESLAGEN_REPEAT, //int       turretFireSpeed;
        WP_TESLAGEN, //weapon_t  turretProjType;
        0.95f, //float     minNormal;
        qfalse, //qboolean  invertNormal;
        qfalse, //qboolean  creepTest;
        0, //int       creepSize;
        qtrue, //qboolean  dccTest;
        qtrue, //qboolean  transparentTest;
        qfalse, //qboolean  reactorTest;
        qfalse, //qboolean  replacable;
    },
    { BA_H_DCC, //int       buildNum;
        "dcc", //char      *buildName;
        "Defence Computer", //char      *humanName;
        "team_human_dcc", //char      *entityName;
        { "models/buildables/dcc/dcc.md3", 0, 0, 0 }, 1.0f, //float     modelScale;
        { -35, -35, -13 }, //vec3_t    mins;
        { 35, 35, 47 }, //vec3_t    maxs;
        0.0f, //float     zOffset;
        TR_GRAVITY, //trType_t  traj;
        0.0, //float     bounce;
        DC_BP, //int       buildPoints;
        (1 << S1) | (1 << S2) | (1 << S3), //int       stages
        DC_HEALTH, //int       health;
        0, //int       regenRate;
        DC_SPLASHDAMAGE, //int       splashDamage;
        DC_SPLASHRADIUS, //int       splashRadius;
        MOD_HSPAWN, //int       meansOfDeath;
        BIT_HUMANS, //int       team;
        (1 << WP_HBUILD2), //weapon_t  buildWeapon;
        BANIM_IDLE1, //int       idleAnim;
        100, //int       nextthink;
        DC_BT, //int       buildTime;
        qfalse, //qboolean  usable;
        0, //int       turretRange;
        0, //int       turretFireSpeed;
        WP_NONE, //weapon_t  turretProjType;
        0.95f, //float     minNormal;
        qfalse, //qboolean  invertNormal;
        qfalse, //qboolean  creepTest;
        0, //int       creepSize;
        qfalse, //qboolean  dccTest;
        qfalse, //qboolean  transparentTest;
        qfalse, //qboolean  reactorTest;
        qtrue, //qboolean  replacable;
    },
    { BA_H_ARMOURY, //int       buildNum;
        "arm", //char      *buildName;
        "Armoury", //char      *humanName;
        "team_human_armoury", //char      *entityName;
        { "models/buildables/arm/arm.md3", 0, 0, 0 }, 1.0f, //float     modelScale;
        { -40, -40, -13 }, //vec3_t    mins;
        { 40, 40, 50 }, //vec3_t    maxs;
        0.0f, //float     zOffset;
        TR_GRAVITY, //trType_t  traj;
        0.0, //float     bounce;
        ARMOURY_BP, //int       buildPoints;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        ARMOURY_HEALTH, //int       health;
        0, //int       regenRate;
        ARMOURY_SPLASHDAMAGE, //int       splashDamage;
        ARMOURY_SPLASHRADIUS, //int       splashRadius;
        MOD_HSPAWN, //int       meansOfDeath;
        BIT_HUMANS, //int       team;
        (1 << WP_HBUILD) | (1 << WP_HBUILD2), //weapon_t  buildWeapon;
        BANIM_IDLE1, //int       idleAnim;
        100, //int       nextthink;
        ARMOURY_BT, //int       buildTime;
        qtrue, //qboolean  usable;
        0, //int       turretRange;
        0, //int       turretFireSpeed;
        WP_NONE, //weapon_t  turretProjType;
        0.95f, //float     minNormal;
        qfalse, //qboolean  invertNormal;
        qfalse, //qboolean  creepTest;
        0, //int       creepSize;
        qfalse, //qboolean  dccTest;
        qfalse, //qboolean  transparentTest;
        qfalse, //qboolean  reactorTest;
        qtrue, //qboolean  replacable;
    },
    { BA_H_REACTOR, //int       buildNum;
        "reactor", //char      *buildName;
        "Reactor", //char      *humanName;
        "team_human_reactor", //char      *entityName;
        { "models/buildables/reactor/reactor.md3", 0, 0, 0 }, 1.0f, //float     modelScale;
        { -50, -50, -15 }, //vec3_t    mins;
        { 50, 50, 95 }, //vec3_t    maxs;
        0.0f, //float     zOffset;
        TR_GRAVITY, //trType_t  traj;
        0.0, //float     bounce;
        REACTOR_BP, //int       buildPoints;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        REACTOR_HEALTH, //int       health;
        0, //int       regenRate;
        REACTOR_SPLASHDAMAGE, //int       splashDamage;
        REACTOR_SPLASHRADIUS, //int       splashRadius;
        MOD_HSPAWN, //int       meansOfDeath;
        BIT_HUMANS, //int       team;
        (1 << WP_HBUILD) | (1 << WP_HBUILD2), //weapon_t  buildWeapon;
        BANIM_IDLE1, //int       idleAnim;
        REACTOR_ATTACK_REPEAT, //int       nextthink;
        REACTOR_BT, //int       buildTime;
        qtrue, //qboolean  usable;
        0, //int       turretRange;
        0, //int       turretFireSpeed;
        WP_NONE, //weapon_t  turretProjType;
        0.95f, //float     minNormal;
        qfalse, //qboolean  invertNormal;
        qfalse, //qboolean  creepTest;
        0, //int       creepSize;
        qfalse, //qboolean  dccTest;
        qfalse, //qboolean  transparentTest;
        qtrue, //qboolean  reactorTest;
        qtrue, //qboolean  replacable;
    },
    { BA_H_REPEATER, //int       buildNum;
        "repeater", //char      *buildName;
        "Repeater", //char      *humanName;
        "team_human_repeater", //char      *entityName;
        { "models/buildables/repeater/repeater.md3", 0, 0, 0 }, 1.0f, //float     modelScale;
        { -15, -15, -15 }, //vec3_t    mins;
        { 15, 15, 25 }, //vec3_t    maxs;
        0.0f, //float     zOffset;
        TR_GRAVITY, //trType_t  traj;
        0.0, //float     bounce;
        REPEATER_BP, //int       buildPoints;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        REPEATER_HEALTH, //int       health;
        0, //int       regenRate;
        REPEATER_SPLASHDAMAGE, //int       splashDamage;
        REPEATER_SPLASHRADIUS, //int       splashRadius;
        MOD_HSPAWN, //int       meansOfDeath;
        BIT_HUMANS, //int       team;
        (1 << WP_HBUILD) | (1 << WP_HBUILD2), //weapon_t  buildWeapon;
        BANIM_IDLE1, //int       idleAnim;
        100, //int       nextthink;
        REPEATER_BT, //int       buildTime;
        qtrue, //qboolean  usable;
        0, //int       turretRange;
        0, //int       turretFireSpeed;
        WP_NONE, //weapon_t  turretProjType;
        0.95f, //float     minNormal;
        qfalse, //qboolean  invertNormal;
        qfalse, //qboolean  creepTest;
        0, //int       creepSize;
        qfalse, //qboolean  dccTest;
        qfalse, //qboolean  transparentTest;
        qfalse, //qboolean  reactorTest;
        qtrue, //qboolean  replacable;
    },
    { BA_I_MACHINEGUN, //int       buildNum;
        "machinegun", //char      *buildName; //This must the same as in the wp_ struct to make the mode works.
        "Rifle", //char      *humanName;
        "team_human_rifle", //char      *entityName;
        { "models/weapons/machinegun/machinegun.md3", 0, 0, 0 }, 1.3f, //float     modelScale;
        { -15, -15, -15 }, //vec3_t    mins;
        { 15, 15, 25 }, //vec3_t    maxs;
        0.0f, //float     zOffset;
        TR_GRAVITY, //trType_t  traj;
        0.0, //float     bounce;
        REPEATER_BP, //int       buildPoints;
        4, //int  stages
        REPEATER_HEALTH, //int       health;
        0, //int       regenRate;
        REPEATER_SPLASHDAMAGE, //int       splashDamage;
        REPEATER_SPLASHRADIUS, //int       splashRadius;
        MOD_HSPAWN, //int       meansOfDeath;
        BIT_HUMANS, //int       team;
        WP_NONE, //weapon_t  buildWeapon;
        BANIM_IDLE1, //int       idleAnim;
        100, //int       nextthink;
        REPEATER_BT, //int       buildTime;
        qtrue, //qboolean  usable;
        0, //int       turretRange;
        0, //int       turretFireSpeed;
        WP_NONE, //weapon_t  turretProjType;
        0.95f, //float     minNormal;
        qfalse, //qboolean  invertNormal;
        qfalse, //qboolean  creepTest;
        0, //int       creepSize;
        qfalse, //qboolean  dccTest;
        qfalse, //qboolean  transparentTest;
        qfalse, //qboolean  reactorTest;
        qtrue, //qboolean  replacable;
    },
    { BA_I_SHOTGUN, //int       buildNum;
        "shotty", //char      *buildName; //This must the same as in the wp_ struct to make the mode works.
        "Shotty", //char      *humanName;
        "team_human_shotgun", //char      *entityName;
        { "models/weapons/shotty/shotty.md3", 0, 0, 0 }, 1.3f, //float     modelScale;
        { -15, -15, -15 }, //vec3_t    mins;
        { 15, 15, 25 }, //vec3_t    maxs;
        0.0f, //float     zOffset;
        TR_GRAVITY, //trType_t  traj;
        0.0, //float     bounce;
        REPEATER_BP, //int       buildPoints;
        4, //int  stages
        REPEATER_HEALTH, //int       health;
        0, //int       regenRate;
        REPEATER_SPLASHDAMAGE, //int       splashDamage;
        REPEATER_SPLASHRADIUS, //int       splashRadius;
        MOD_HSPAWN, //int       meansOfDeath;
        BIT_HUMANS, //int       team;
        WP_NONE, //weapon_t  buildWeapon;
        BANIM_IDLE1, //int       idleAnim;
        100, //int       nextthink;
        REPEATER_BT, //int       buildTime;
        qtrue, //qboolean  usable;
        0, //int       turretRange;
        0, //int       turretFireSpeed;
        WP_NONE, //weapon_t  turretProjType;
        0.95f, //float     minNormal;
        qfalse, //qboolean  invertNormal;
        qfalse, //qboolean  creepTest;
        0, //int       creepSize;
        qfalse, //qboolean  dccTest;
        qfalse, //qboolean  transparentTest;
        qfalse, //qboolean  reactorTest;
        qtrue, //qboolean  replacable;
    },
    { BA_I_AXE, //int       buildNum;
        "axe", //char      *buildName; //This must the same as in the wp_ struct to make the mode works.
        "Axe", //char      *humanName;
        "team_human_shotgun", //char      *entityName;
        { "models/weapons/axe/axe.md3", 0, 0, 0 }, 1.3f, //float     modelScale;
        { -15, -15, -15 }, //vec3_t    mins;
        { 15, 15, 25 }, //vec3_t    maxs;
        0.0f, //float     zOffset;
        TR_GRAVITY, //trType_t  traj;
        0.0, //float     bounce;
        REPEATER_BP, //int       buildPoints;
        4, //int  stages
        REPEATER_HEALTH, //int       health;
        0, //int       regenRate;
        REPEATER_SPLASHDAMAGE, //int       splashDamage;
        REPEATER_SPLASHRADIUS, //int       splashRadius;
        MOD_HSPAWN, //int       meansOfDeath;
        BIT_HUMANS, //int       team;
        WP_NONE, //weapon_t  buildWeapon;
        BANIM_IDLE1, //int       idleAnim;
        100, //int       nextthink;
        REPEATER_BT, //int       buildTime;
        qtrue, //qboolean  usable;
        0, //int       turretRange;
        0, //int       turretFireSpeed;
        WP_NONE, //weapon_t  turretProjType;
        0.95f, //float     minNormal;
        qfalse, //qboolean  invertNormal;
        qfalse, //qboolean  creepTest;
        0, //int       creepSize;
        qfalse, //qboolean  dccTest;
        qfalse, //qboolean  transparentTest;
        qfalse, //qboolean  reactorTest;
        qtrue, //qboolean  replacable;
    },
    { BA_I_CHAINGUN, //int       buildNum;
        "chaingun", //char      *buildName; //This must the same as in the wp_ struct to make the mode works.
        "Chaingun", //char      *humanName;
        "team_human_chaingun", //char      *entityName;
        { "models/weapons/chaingun/chaingun.md3", 0, 0, 0 }, 1.3f, //float     modelScale;
        { -15, -15, -15 }, //vec3_t    mins;
        { 15, 15, 25 }, //vec3_t    maxs;
        0.0f, //float     zOffset;
        TR_GRAVITY, //trType_t  traj;
        0.0, //float     bounce;
        REPEATER_BP, //int       buildPoints;
        4, //int  stages
        REPEATER_HEALTH, //int       health;
        0, //int       regenRate;
        REPEATER_SPLASHDAMAGE, //int       splashDamage;
        REPEATER_SPLASHRADIUS, //int       splashRadius;
        MOD_HSPAWN, //int       meansOfDeath;
        BIT_HUMANS, //int       team;
        WP_NONE, //weapon_t  buildWeapon;
        BANIM_IDLE1, //int       idleAnim;
        100, //int       nextthink;
        REPEATER_BT, //int       buildTime;
        qtrue, //qboolean  usable;
        0, //int       turretRange;
        0, //int       turretFireSpeed;
        WP_NONE, //weapon_t  turretProjType;
        0.95f, //float     minNormal;
        qfalse, //qboolean  invertNormal;
        qfalse, //qboolean  creepTest;
        0, //int       creepSize;
        qfalse, //qboolean  dccTest;
        qfalse, //qboolean  transparentTest;
        qfalse, //qboolean  reactorTest;
        qtrue, //qboolean  replacable;
    },
    { BA_I_MDRIVER, //int       buildNum;
        "mdriver", //char      *buildName; //This must the same as in the wp_ struct to make the mode works.
        "Mdriver", //char      *humanName;
        "team_human_mdriver", //char      *entityName;
        { "models/weapons/mdriver/mdriver.md3", 0, 0, 0 }, 1.3f, //float     modelScale;
        { -15, -15, -15 }, //vec3_t    mins;
        { 15, 15, 25 }, //vec3_t    maxs;
        0.0f, //float     zOffset;
        TR_GRAVITY, //trType_t  traj;
        0.0, //float     bounce;
        REPEATER_BP, //int       buildPoints;
        4, //int  stages
        REPEATER_HEALTH, //int       health;
        0, //int       regenRate;
        REPEATER_SPLASHDAMAGE, //int       splashDamage;
        REPEATER_SPLASHRADIUS, //int       splashRadius;
        MOD_HSPAWN, //int       meansOfDeath;
        BIT_HUMANS, //int       team;
        WP_NONE, //weapon_t  buildWeapon;
        BANIM_IDLE1, //int       idleAnim;
        100, //int       nextthink;
        REPEATER_BT, //int       buildTime;
        qtrue, //qboolean  usable;
        0, //int       turretRange;
        0, //int       turretFireSpeed;
        WP_NONE, //weapon_t  turretProjType;
        0.95f, //float     minNormal;
        qfalse, //qboolean  invertNormal;
        qfalse, //qboolean  creepTest;
        0, //int       creepSize;
        qfalse, //qboolean  dccTest;
        qfalse, //qboolean  transparentTest;
        qfalse, //qboolean  reactorTest;
        qtrue, //qboolean  replacable;
    },
    { BA_I_LASERGUN, //int       buildNum;
        "lasergun", //char      *buildName; //This must the same as in the wp_ struct to make the mode works.
        "Laser gun", //char      *humanName;
        "team_human_lgun", //char      *entityName;
        { "models/weapons/lasergun/lasergun.md3", 0, 0, 0 }, 1.3f, //float     modelScale;
        { -15, -15, -15 }, //vec3_t    mins;
        { 15, 15, 25 }, //vec3_t    maxs;
        0.0f, //float     zOffset;
        TR_GRAVITY, //trType_t  traj;
        0.0, //float     bounce;
        REPEATER_BP, //int       buildPoints;
        4, //int  stages
        REPEATER_HEALTH, //int       health;
        0, //int       regenRate;
        REPEATER_SPLASHDAMAGE, //int       splashDamage;
        REPEATER_SPLASHRADIUS, //int       splashRadius;
        MOD_HSPAWN, //int       meansOfDeath;
        BIT_HUMANS, //int       team;
        WP_NONE, //weapon_t  buildWeapon;
        BANIM_IDLE1, //int       idleAnim;
        100, //int       nextthink;
        REPEATER_BT, //int       buildTime;
        qtrue, //qboolean  usable;
        0, //int       turretRange;
        0, //int       turretFireSpeed;
        WP_NONE, //weapon_t  turretProjType;
        0.95f, //float     minNormal;
        qfalse, //qboolean  invertNormal;
        qfalse, //qboolean  creepTest;
        0, //int       creepSize;
        qfalse, //qboolean  dccTest;
        qfalse, //qboolean  transparentTest;
        qfalse, //qboolean  reactorTest;
        qtrue, //qboolean  replacable;
    },
    { BA_I_SYRINX, //int       buildNum;
        "syrinx", //char      *buildName; //This must the same as in the wp_ struct to make the mode works.
        "Syrinx", //char      *humanName;
        "team_human_syrinx", //char      *entityName;
        { "models/weapons/syrinx/syrinx.md3", 0, 0, 0 }, 1.3f, //float     modelScale;
        { -15, -15, -15 }, //vec3_t    mins;
        { 15, 15, 25 }, //vec3_t    maxs;
        0.0f, //float     zOffset;
        TR_GRAVITY, //trType_t  traj;
        0.0, //float     bounce;
        REPEATER_BP, //int       buildPoints;
        4, //int  stages
        REPEATER_HEALTH, //int       health;
        0, //int       regenRate;
        REPEATER_SPLASHDAMAGE, //int       splashDamage;
        REPEATER_SPLASHRADIUS, //int       splashRadius;
        MOD_HSPAWN, //int       meansOfDeath;
        BIT_HUMANS, //int       team;
        WP_NONE, //weapon_t  buildWeapon;
        BANIM_IDLE1, //int       idleAnim;
        100, //int       nextthink;
        REPEATER_BT, //int       buildTime;
        qtrue, //qboolean  usable;
        0, //int       turretRange;
        0, //int       turretFireSpeed;
        WP_NONE, //weapon_t  turretProjType;
        0.95f, //float     minNormal;
        qfalse, //qboolean  invertNormal;
        qfalse, //qboolean  creepTest;
        0, //int       creepSize;
        qfalse, //qboolean  dccTest;
        qfalse, //qboolean  transparentTest;
        qfalse, //qboolean  reactorTest;
        qtrue, //qboolean  replacable;
    },
    { BA_I_GRENADE_LAUNCHER, //int       buildNum;
        "grenade_launcher", //char      *buildName; //This must the same as in the wp_ struct to make the mode works.
        "Grenade Launcher", //char      *humanName;
        "team_human_launcher", //char      *entityName;
        { "models/weapons/grenade_launcher/grenade_launcher.md3", 0, 0, 0 }, 1.3f, //float     modelScale;
        { -15, -15, -15 }, //vec3_t    mins;
        { 15, 15, 25 }, //vec3_t    maxs;
        0.0f, //float     zOffset;
        TR_GRAVITY, //trType_t  traj;
        0.0, //float     bounce;
        REPEATER_BP, //int       buildPoints;
        4, //int  stages
        REPEATER_HEALTH, //int       health;
        0, //int       regenRate;
        REPEATER_SPLASHDAMAGE, //int       splashDamage;
        REPEATER_SPLASHRADIUS, //int       splashRadius;
        MOD_HSPAWN, //int       meansOfDeath;
        BIT_HUMANS, //int       team;
        WP_NONE, //weapon_t  buildWeapon;
        BANIM_IDLE1, //int       idleAnim;
        100, //int       nextthink;
        REPEATER_BT, //int       buildTime;
        qtrue, //qboolean  usable;
        0, //int       turretRange;
        0, //int       turretFireSpeed;
        WP_NONE, //weapon_t  turretProjType;
        0.95f, //float     minNormal;
        qfalse, //qboolean  invertNormal;
        qfalse, //qboolean  creepTest;
        0, //int       creepSize;
        qfalse, //qboolean  dccTest;
        qfalse, //qboolean  transparentTest;
        qfalse, //qboolean  reactorTest;
        qtrue, //qboolean  replacable;
    },
    { BA_I_MINE, //int       buildNum;
        "mine", //char      *buildName; //This must the same as in the wp_ struct to make the mode works.
        "Mine", //char      *humanName;
        "team_human_mine", //char      *entityName;
        { "models/weapons/mine/mine.md3", 0, 0, 0 }, 1.3f, //float     modelScale;
        { -15, -15, -15 }, //vec3_t    mins;
        { 15, 15, 25 }, //vec3_t    maxs;
        0.0f, //float     zOffset;
        TR_GRAVITY, //trType_t  traj;
        0.0, //float     bounce;
        REPEATER_BP, //int       buildPoints;
        4, //int  stages
        REPEATER_HEALTH, //int       health;
        0, //int       regenRate;
        REPEATER_SPLASHDAMAGE, //int       splashDamage;
        REPEATER_SPLASHRADIUS, //int       splashRadius;
        MOD_HSPAWN, //int       meansOfDeath;
        BIT_HUMANS, //int       team;
        WP_NONE, //weapon_t  buildWeapon;
        BANIM_IDLE1, //int       idleAnim;
        100, //int       nextthink;
        REPEATER_BT, //int       buildTime;
        qtrue, //qboolean  usable;
        0, //int       turretRange;
        0, //int       turretFireSpeed;
        WP_NONE, //weapon_t  turretProjType;
        0.95f, //float     minNormal;
        qfalse, //qboolean  invertNormal;
        qfalse, //qboolean  creepTest;
        0, //int       creepSize;
        qfalse, //qboolean  dccTest;
        qfalse, //qboolean  transparentTest;
        qfalse, //qboolean  reactorTest;
        qtrue, //qboolean  replacable;
    },
    { BA_I_ROCKET_LAUNCHER, //int       buildNum;
        "rocket_launcher", //char      *buildName; //This must the same as in the wp_ struct to make the mode works.
        "Rocket Launcher", //char      *humanName;
        "team_human_rocket_launcher", //char      *entityName;
        { "models/weapons/rocket_launcher/rocket_launcher.md3", 0, 0, 0 }, 1.3f, //float     modelScale;
        { -15, -15, -15 }, //vec3_t    mins;
        { 15, 15, 25 }, //vec3_t    maxs;
        0.0f, //float     zOffset;
        TR_GRAVITY, //trType_t  traj;
        0.0, //float     bounce;
        REPEATER_BP, //int       buildPoints;
        4, //int  stages
        REPEATER_HEALTH, //int       health;
        0, //int       regenRate;
        REPEATER_SPLASHDAMAGE, //int       splashDamage;
        REPEATER_SPLASHRADIUS, //int       splashRadius;
        MOD_HSPAWN, //int       meansOfDeath;
        BIT_HUMANS, //int       team;
        WP_NONE, //weapon_t  buildWeapon;
        BANIM_IDLE1, //int       idleAnim;
        100, //int       nextthink;
        REPEATER_BT, //int       buildTime;
        qtrue, //qboolean  usable;
        0, //int       turretRange;
        0, //int       turretFireSpeed;
        WP_NONE, //weapon_t  turretProjType;
        0.95f, //float     minNormal;
        qfalse, //qboolean  invertNormal;
        qfalse, //qboolean  creepTest;
        0, //int       creepSize;
        qfalse, //qboolean  dccTest;
        qfalse, //qboolean  transparentTest;
        qfalse, //qboolean  reactorTest;
        qtrue, //qboolean  replacable;
    }, };
//"rocket_launcher", //char      *weaponName;
//       "Rocket Launcher", //char      *weaponHumanName;
int bg_numBuildables = sizeof(bg_buildableList) / sizeof(bg_buildableList[0]);

//separate from bg_buildableList to work around char struct init bug
buildableAttributeOverrides_t bg_buildableOverrideList[BA_NUM_BUILDABLES];

/*
 ==============
 BG_FindBuildNumForName
 ==============
 */
int
BG_FindBuildNumForName(char *name)
{
  int i;

  for(i = 0;i < bg_numBuildables;i++)
  {
    if (!Q_stricmp(bg_buildableList[i].buildName, name))
      return bg_buildableList[i].buildNum;
  }

  //wimp out
  return BA_NONE;
}

/*
 ==============
 BG_FindBuildNumForEntityName
 ==============
 */
int
BG_FindBuildNumForEntityName(char *name)
{
  int i;

  for(i = 0;i < bg_numBuildables;i++)
  {
    if (!Q_stricmp(bg_buildableList[i].entityName, name))
      return bg_buildableList[i].buildNum;
  }

  //wimp out
  return BA_NONE;
}

/*
 ==============
 BG_FindNameForBuildNum
 ==============
 */
char *
BG_FindNameForBuildable(int bclass)
{
  int i;

  for(i = 0;i < bg_numBuildables;i++)
  {
    if (bg_buildableList[i].buildNum == bclass)
      return bg_buildableList[i].buildName;
  }

  //wimp out
  return 0;
}

/*
 ==============
 BG_FindHumanNameForBuildNum
 ==============
 */
char *
BG_FindHumanNameForBuildable(int bclass)
{
  int i;

  for(i = 0;i < bg_numBuildables;i++)
  {
    if (bg_buildableList[i].buildNum == bclass)
      return bg_buildableList[i].humanName;
  }

  //wimp out
  return 0;
}

/*
 ==============
 BG_FindEntityNameForBuildNum
 ==============
 */
char *
BG_FindEntityNameForBuildable(int bclass)
{
  int i;

  for(i = 0;i < bg_numBuildables;i++)
  {
    if (bg_buildableList[i].buildNum == bclass)
      return bg_buildableList[i].entityName;
  }

  //wimp out
  return 0;
}

/*
 ==============
 BG_FindModelsForBuildNum
 ==============
 */
char *
BG_FindModelsForBuildable(int bclass, int modelNum)
{
  int i;

  if (bg_buildableOverrideList[bclass].models[modelNum][0] != 0)
    return bg_buildableOverrideList[bclass].models[modelNum];

  for(i = 0;i < bg_numBuildables;i++)
  {
    if (bg_buildableList[i].buildNum == bclass)
      return bg_buildableList[i].models[modelNum];
  }

  //wimp out
  return 0;
}

/*
 ==============
 BG_FindModelScaleForBuildable
 ==============
 */
float
BG_FindModelScaleForBuildable(int bclass)
{
  int i;

  if (bg_buildableOverrideList[bclass].modelScale != 0.0f)
    return bg_buildableOverrideList[bclass].modelScale;

  for(i = 0;i < bg_numBuildables;i++)
  {
    if (bg_buildableList[i].buildNum == bclass)
      return bg_buildableList[i].modelScale;
  }

  Com_Printf(S_COLOR_YELLOW "WARNING: fallthrough in BG_FindModelScaleForBuildable( %d )\n", bclass);
  return 1.0f;
}

/*
 ==============
 BG_FindBBoxForBuildable
 ==============
 */
void
BG_FindBBoxForBuildable(int bclass, vec3_t mins, vec3_t maxs)
{
  int i;

  for(i = 0;i < bg_numBuildables;i++)
  {
    if (bg_buildableList[i].buildNum == bclass)
    {
      if (mins != NULL)
      {
        VectorCopy(bg_buildableList[ i ].mins, mins);

        if (VectorLength(bg_buildableOverrideList[bclass].mins))
          VectorCopy(bg_buildableOverrideList[ bclass ].mins, mins);
      }

      if (maxs != NULL)
      {
        VectorCopy(bg_buildableList[ i ].maxs, maxs);

        if (VectorLength(bg_buildableOverrideList[bclass].maxs))
          VectorCopy(bg_buildableOverrideList[ bclass ].maxs, maxs);
      }

      return;
    }
  }

  if (mins != NULL)
    VectorCopy(bg_buildableList[ 0 ].mins, mins);

  if (maxs != NULL)
    VectorCopy(bg_buildableList[ 0 ].maxs, maxs);
}

/*
 ==============
 BG_FindZOffsetForBuildable
 ==============
 */
float
BG_FindZOffsetForBuildable(int bclass)
{
  int i;

  if (bg_buildableOverrideList[bclass].zOffset != 0.0f)
    return bg_buildableOverrideList[bclass].zOffset;

  for(i = 0;i < bg_numBuildables;i++)
  {
    if (bg_buildableList[i].buildNum == bclass)
    {
      return bg_buildableList[i].zOffset;
    }
  }

  return 0.0f;
}

/*
 ==============
 BG_FindTrajectoryForBuildable
 ==============
 */
trType_t
BG_FindTrajectoryForBuildable(int bclass)
{
  int i;

  for(i = 0;i < bg_numBuildables;i++)
  {
    if (bg_buildableList[i].buildNum == bclass)
    {
      return bg_buildableList[i].traj;
    }
  }

  return TR_GRAVITY;
}

/*
 ==============
 BG_FindBounceForBuildable
 ==============
 */
float
BG_FindBounceForBuildable(int bclass)
{
  int i;

  for(i = 0;i < bg_numBuildables;i++)
  {
    if (bg_buildableList[i].buildNum == bclass)
    {
      return bg_buildableList[i].bounce;
    }
  }

  return 0.0;
}

/*
 ==============
 BG_FindBuildPointsForBuildable
 ==============
 */
int
BG_FindBuildPointsForBuildable(int bclass)
{
  int i;

  for(i = 0;i < bg_numBuildables;i++)
  {
    if (bg_buildableList[i].buildNum == bclass)
    {
      return bg_buildableList[i].buildPoints;
    }
  }

  return 1000;
}

/*
 ==============
 BG_FindStagesForBuildable
 ==============
 */
qboolean
BG_FindStagesForBuildable(int bclass, stage_t stage)
{
  int i;

  for(i = 0;i < bg_numBuildables;i++)
  {
    if (bg_buildableList[i].buildNum == bclass)
    {
      if (bg_buildableList[i].stages & (1 << stage))
        return qtrue;
      else
        return qfalse;
    }
  }

  return qfalse;
}

/*
 ==============
 BG_FindHealthForBuildable
 ==============
 */
int
BG_FindHealthForBuildable(int bclass)
{
  int i;

  for(i = 0;i < bg_numBuildables;i++)
  {
    if (bg_buildableList[i].buildNum == bclass)
    {
      return bg_buildableList[i].health;
    }
  }

  return 1000;
}

/*
 ==============
 BG_FindRegenRateForBuildable
 ==============
 */
int
BG_FindRegenRateForBuildable(int bclass)
{
  int i;

  for(i = 0;i < bg_numBuildables;i++)
  {
    if (bg_buildableList[i].buildNum == bclass)
    {
      return bg_buildableList[i].regenRate;
    }
  }

  return 0;
}

/*
 ==============
 BG_FindSplashDamageForBuildable
 ==============
 */
int
BG_FindSplashDamageForBuildable(int bclass)
{
  int i;

  for(i = 0;i < bg_numBuildables;i++)
  {
    if (bg_buildableList[i].buildNum == bclass)
    {
      return bg_buildableList[i].splashDamage;
    }
  }

  return 50;
}

/*
 ==============
 BG_FindSplashRadiusForBuildable
 ==============
 */
int
BG_FindSplashRadiusForBuildable(int bclass)
{
  int i;

  for(i = 0;i < bg_numBuildables;i++)
  {
    if (bg_buildableList[i].buildNum == bclass)
    {
      return bg_buildableList[i].splashRadius;
    }
  }

  return 200;
}

/*
 ==============
 BG_FindMODForBuildable
 ==============
 */
int
BG_FindMODForBuildable(int bclass)
{
  int i;

  for(i = 0;i < bg_numBuildables;i++)
  {
    if (bg_buildableList[i].buildNum == bclass)
    {
      return bg_buildableList[i].meansOfDeath;
    }
  }

  return MOD_UNKNOWN;
}

/*
 ==============
 BG_FindTeamForBuildable
 ==============
 */
int
BG_FindTeamForBuildable(int bclass)
{
  int i;

  for(i = 0;i < bg_numBuildables;i++)
  {
    if (bg_buildableList[i].buildNum == bclass)
    {
      return bg_buildableList[i].team;
    }
  }

  return BIT_NONE;
}

/*
 ==============
 BG_FindBuildWeaponForBuildable
 ==============
 */
weapon_t
BG_FindBuildWeaponForBuildable(int bclass)
{
  int i;

  for(i = 0;i < bg_numBuildables;i++)
  {
    if (bg_buildableList[i].buildNum == bclass)
    {
      return bg_buildableList[i].buildWeapon;
    }
  }

  return BA_NONE;
}

/*
 ==============
 BG_FindAnimForBuildable
 ==============
 */
int
BG_FindAnimForBuildable(int bclass)
{
  int i;

  for(i = 0;i < bg_numBuildables;i++)
  {
    if (bg_buildableList[i].buildNum == bclass)
    {
      return bg_buildableList[i].idleAnim;
    }
  }

  return BANIM_IDLE1;
}

/*
 ==============
 BG_FindNextThinkForBuildable
 ==============
 */
int
BG_FindNextThinkForBuildable(int bclass)
{
  int i;

  for(i = 0;i < bg_numBuildables;i++)
  {
    if (bg_buildableList[i].buildNum == bclass)
    {
      return bg_buildableList[i].nextthink;
    }
  }

  return 100;
}

/*
 ==============
 BG_FindBuildTimeForBuildable
 ==============
 */
int
BG_FindBuildTimeForBuildable(int bclass)
{
  int i;

  for(i = 0;i < bg_numBuildables;i++)
  {
    if (bg_buildableList[i].buildNum == bclass)
    {
      return bg_buildableList[i].buildTime;
    }
  }

  return 10000;
}

/*
 ==============
 BG_FindUsableForBuildable
 ==============
 */
qboolean
BG_FindUsableForBuildable(int bclass)
{
  int i;

  for(i = 0;i < bg_numBuildables;i++)
  {
    if (bg_buildableList[i].buildNum == bclass)
    {
      return bg_buildableList[i].usable;
    }
  }

  return qfalse;
}

/*
 ==============
 BG_FindFireSpeedForBuildable
 ==============
 */
int
BG_FindFireSpeedForBuildable(int bclass)
{
  int i;

  for(i = 0;i < bg_numBuildables;i++)
  {
    if (bg_buildableList[i].buildNum == bclass)
    {
      return bg_buildableList[i].turretFireSpeed;
    }
  }

  return 1000;
}

/*
 ==============
 BG_FindRangeForBuildable
 ==============
 */
int
BG_FindRangeForBuildable(int bclass)
{
  int i;

  for(i = 0;i < bg_numBuildables;i++)
  {
    if (bg_buildableList[i].buildNum == bclass)
    {
      return bg_buildableList[i].turretRange;
    }
  }

  return 1000;
}

/*
 ==============
 BG_FindProjTypeForBuildable
 ==============
 */
weapon_t
BG_FindProjTypeForBuildable(int bclass)
{
  int i;

  for(i = 0;i < bg_numBuildables;i++)
  {
    if (bg_buildableList[i].buildNum == bclass)
    {
      return bg_buildableList[i].turretProjType;
    }
  }

  return WP_NONE;
}

/*
 ==============
 BG_FindMinNormalForBuildable
 ==============
 */
float
BG_FindMinNormalForBuildable(int bclass)
{
  int i;

  for(i = 0;i < bg_numBuildables;i++)
  {
    if (bg_buildableList[i].buildNum == bclass)
    {
      return bg_buildableList[i].minNormal;
    }
  }

  return 0.707f;
}

/*
 ==============
 BG_FindInvertNormalForBuildable
 ==============
 */
qboolean
BG_FindInvertNormalForBuildable(int bclass)
{
  int i;

  for(i = 0;i < bg_numBuildables;i++)
  {
    if (bg_buildableList[i].buildNum == bclass)
    {
      return bg_buildableList[i].invertNormal;
    }
  }

  return qfalse;
}

/*
 ==============
 BG_FindCreepTestForBuildable
 ==============
 */
int
BG_FindCreepTestForBuildable(int bclass)
{
  int i;

  for(i = 0;i < bg_numBuildables;i++)
  {
    if (bg_buildableList[i].buildNum == bclass)
    {
      return bg_buildableList[i].creepTest;
    }
  }

  return qfalse;
}

/*
 ==============
 BG_FindCreepSizeForBuildable
 ==============
 */
int
BG_FindCreepSizeForBuildable(int bclass)
{
  int i;

  for(i = 0;i < bg_numBuildables;i++)
  {
    if (bg_buildableList[i].buildNum == bclass)
    {
      return bg_buildableList[i].creepSize;
    }
  }

  return CREEP_BASESIZE;
}

/*
 ==============
 BG_FindDCCTestForBuildable
 ==============
 */
int
BG_FindDCCTestForBuildable(int bclass)
{
  int i;

  for(i = 0;i < bg_numBuildables;i++)
  {
    if (bg_buildableList[i].buildNum == bclass)
    {
      return bg_buildableList[i].dccTest;
    }
  }

  return qfalse;
}

/*
 ==============
 BG_FindUniqueTestForBuildable
 ==============
 */
int
BG_FindUniqueTestForBuildable(int bclass)
{
  int i;

  for(i = 0;i < bg_numBuildables;i++)
  {
    if (bg_buildableList[i].buildNum == bclass)
    {
      return bg_buildableList[i].reactorTest;
    }
  }

  return qfalse;
}

/*
 ==============
 BG_FindReplaceableTestForBuildable
 ==============
 */
qboolean
BG_FindReplaceableTestForBuildable(int bclass)
{
  int i;

  for(i = 0;i < bg_numBuildables;i++)
  {
    if (bg_buildableList[i].buildNum == bclass)
    {
      return bg_buildableList[i].replaceable;
    }
  }
  return qfalse;
}

/*
 ==============
 BG_FindOverrideForBuildable
 ==============
 */
static buildableAttributeOverrides_t *
BG_FindOverrideForBuildable(int bclass)
{
  return &bg_buildableOverrideList[bclass];
}

/*
 ==============
 BG_FindTransparentTestForBuildable
 ==============
 */
qboolean
BG_FindTransparentTestForBuildable(int bclass)
{
  int i;

  for(i = 0;i < bg_numBuildables;i++)
  {
    if (bg_buildableList[i].buildNum == bclass)
    {
      return bg_buildableList[i].transparentTest;
    }
  }
  return qfalse;
}

/*
 ======================
 BG_ParseBuildableFile

 Parses a configuration file describing a builable
 ======================
 */
static qboolean
BG_ParseBuildableFile(const char *filename, buildableAttributeOverrides_t *bao)
{
  char *text_p;
  int i;
  int len;
  char *token;
  char text[20000];
  fileHandle_t f;
  float scale;

  // load the file
  len = trap_FS_FOpenFile(filename, &f, FS_READ);
  if (len < 0)
    return qfalse;

  if (len == 0 || len >= sizeof(text) - 1)
  {
    trap_FS_FCloseFile(f);
    Com_Printf(S_COLOR_RED "ERROR: Buildable file %s is %s\n", filename, len == 0 ? "empty"
        : "too long");
    return qfalse;
  }

  trap_FS_Read(text, len, f);
  text[len] = 0;
  trap_FS_FCloseFile(f);

  // parse the text
  text_p = text;

  // read optional parameters
  while(1)
  {
    token = COM_Parse(&text_p);

    if (!token)
      break;

    if (!Q_stricmp(token, ""))
      break;

    if (!Q_stricmp(token, "model"))
    {
      int index = 0;

      token = COM_Parse(&text_p);
      if (!token)
        break;

      index = atoi(token);

      if (index < 0)
        index = 0;
      else if (index > 3)
        index = 3;

      token = COM_Parse(&text_p);
      if (!token)
        break;

      Q_strncpyz(bao->models[index], token, sizeof(bao->models[0]));

      continue;
    }
    else if (!Q_stricmp(token, "modelScale"))
    {
      token = COM_Parse(&text_p);
      if (!token)
        break;

      scale = atof(token);

      if (scale < 0.0f)
        scale = 0.0f;

      bao->modelScale = scale;

      continue;
    }
    else if (!Q_stricmp(token, "mins"))
    {
      for(i = 0;i <= 2;i++)
      {
        token = COM_Parse(&text_p);
        if (!token)
          break;

        bao->mins[i] = atof(token);
      }

      continue;
    }
    else if (!Q_stricmp(token, "maxs"))
    {
      for(i = 0;i <= 2;i++)
      {
        token = COM_Parse(&text_p);
        if (!token)
          break;

        bao->maxs[i] = atof(token);
      }

      continue;
    }
    else if (!Q_stricmp(token, "zOffset"))
    {
      float offset;

      token = COM_Parse(&text_p);
      if (!token)
        break;

      offset = atof(token);

      bao->zOffset = offset;

      continue;
    }

    Com_Printf(S_COLOR_RED "ERROR: unknown token '%s'\n", token);
    return qfalse;
  }

  return qtrue;
}

/*
 ===============
 BG_InitBuildableOverrides

 Set any overrides specfied by file
 ===============
 */
void
BG_InitBuildableOverrides(void)
{
  int i;
  buildableAttributeOverrides_t *bao;

  for(i = BA_NONE + 1;i < BA_NUM_BUILDABLES;i++)
  {
    bao = BG_FindOverrideForBuildable(i);

    BG_ParseBuildableFile(va("overrides/buildables/%s.cfg", BG_FindNameForBuildable(i)), bao);
  }
}

////////////////////////////////////////////////////////////////////////////////

classAttributes_t bg_classList[] =
{
{ PCL_NONE, //int     classnum;
    "spectator", //char    *className;
    "Spectator", //char    *humanName;
    "", //char    *modelname;
    1.0f, //float   modelScale;
    "", //char    *skinname;
    1.0f, //float   shadowScale;
    "", //char    *hudname;
    (1 << S1) | (1 << S2) | (1 << S3), //int  stages
    { -15, -15, -15 }, //vec3_t  mins;
    { 15, 15, 15 }, //vec3_t  maxs;
    { 15, 15, 15 }, //vec3_t  crouchmaxs;
    { -15, -15, -15 }, //vec3_t  deadmins;
    { 15, 15, 15 }, //vec3_t  deadmaxs;
    0.0f, //float   zOffset
    0.f, //float   spriteOffset;
    0, 0, //int     viewheight, crouchviewheight;
    0, //int     health;
    0.0f, //float   fallDamage;
    0, //int     regenRate;
    0, //int     abilities;
    WP_NONE, //weapon_t  startWeapon
    0.0f, //float   buildDist;
    90, //int     fov;
    0.000f, //float   bob;
    1.0f, //float   bobCycle;
    0, //int     steptime;
    600, //float   speed;
    10.0f, //float   acceleration;
    1.0f, //float   airAcceleration;
    6.0f, //float   friction;
    100.0f, //float   stopSpeed;
    270.0f, //float   jumpMagnitude;
    1.0f, //float   knockbackScale;
    { PCL_NONE, PCL_NONE, PCL_NONE }, //int     children[ 3 ];
    0, //int     cost;
    0 //int     value;
    },
    { PCL_ALIEN_BUILDER0, //int     classnum;
        "builder", //char    *className;
        "Builder", //char    *humanName;
        "builder", //char    *modelname;
        1.0f, //float   modelScale;
        "default", //char    *skinname;
        1.0f, //float   shadowScale;
        "alien_builder_hud", //char    *hudname;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        { -15, -15, -20 }, //vec3_t  mins;
        { 15, 15, 20 }, //vec3_t  maxs;
        { 15, 15, 20 }, //vec3_t  crouchmaxs;
        { -15, -15, -4 }, //vec3_t  deadmins;
        { 15, 15, 4 }, //vec3_t  deadmaxs;
        0.0f, //float   zOffset
        0.f, //float   spriteOffset;
        0, 0, //int     viewheight, crouchviewheight;
        ABUILDER_HEALTH, //int     health;
        0.2f, //float   fallDamage;
        ABUILDER_REGEN, //int     regenRate;
        SCA_TAKESFALLDAMAGE | SCA_FOVWARPS | SCA_ALIENSENSE, //int     abilities;
        WP_ABUILD, //weapon_t  startWeapon
        95.0f, //float   buildDist;
        80, //int     fov;
        0.001f, //float   bob;
        2.0f, //float   bobCycle;
        150, //int     steptime;
        ABUILDER_SPEED, //float   speed;
        10.0f, //float   acceleration;
        1.0f, //float   airAcceleration;
        6.0f, //float   friction;
        100.0f, //float   stopSpeed;
        195.0f, //float   jumpMagnitude;
        1.0f, //float   knockbackScale;
        { PCL_ALIEN_BUILDER0_UPG, PCL_ALIEN_LEVEL0, PCL_NONE }, //int     children[ 3 ];
        ABUILDER_COST, //int     cost;
        ABUILDER_VALUE //int     value;
    },
    { PCL_ALIEN_BUILDER0_UPG, //int     classnum;
        "builderupg", //char    *classname;
        "Advanced Builder", //char    *humanname;
        "builder", //char    *modelname;
        1.0f, //float   modelScale;
        "advanced", //char    *skinname;
        1.0f, //float   shadowScale;
        "alien_builder_hud", //char    *hudname;
        (1 << S2) | (1 << S3), //int  stages
        { -20, -20, -20 }, //vec3_t  mins;
        { 20, 20, 20 }, //vec3_t  maxs;
        { 20, 20, 20 }, //vec3_t  crouchmaxs;
        { -20, -20, -4 }, //vec3_t  deadmins;
        { 20, 20, 4 }, //vec3_t  deadmaxs;
        0.0f, //float   zOffset
        0.f, //float   spriteOffset;
        0, 0, //int     viewheight, crouchviewheight;
        ABUILDER_UPG_HEALTH, //int     health;
        0.0f, //float   fallDamage;
        ABUILDER_UPG_REGEN, //int     regenRate;
        SCA_FOVWARPS | SCA_WALLCLIMBER | SCA_ALIENSENSE, //int     abilities;
        WP_ABUILD2, //weapon_t  startWeapon
        105.0f, //float   buildDist;
        110, //int     fov;
        0.001f, //float   bob;
        2.0f, //float   bobCycle;
        100, //int     steptime;
        ABUILDER_UPG_SPEED, //float   speed;
        10.0f, //float   acceleration;
        1.0f, //float   airAcceleration;
        6.0f, //float   friction;
        100.0f, //float   stopSpeed;
        270.0f, //float   jumpMagnitude;
        1.0f, //float   knockbackScale;
        { PCL_ALIEN_LEVEL0, PCL_NONE, PCL_NONE }, //int     children[ 3 ];
        ABUILDER_UPG_COST, //int     cost;
        ABUILDER_UPG_VALUE //int     value;
    },
    { PCL_ALIEN_LEVEL0, //int     classnum;
        "level0", //char    *classname;
        "Soldier", //char    *humanname;
        "jumper", //char    *modelname;
        0.2f, //float   modelScale;
        "default", //char    *skinname;
        0.3f, //float   shadowScale;
        "alien_general_hud", //char    *hudname;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        { -15, -15, -15 }, //vec3_t  mins;
        { 15, 15, 15 }, //vec3_t  maxs;
        { 15, 15, 15 }, //vec3_t  crouchmaxs;
        { -15, -15, -4 }, //vec3_t  deadmins;
        { 15, 15, 4 }, //vec3_t  deadmaxs;
        -8.0f, //float   zOffset
        0.f, //float   spriteOffset;
        0, 0, //int     viewheight, crouchviewheight;
        LEVEL0_HEALTH, //int     health;
        0.0f, //float   fallDamage;
        LEVEL0_REGEN, //int     regenRate;
        SCA_WALLCLIMBER | SCA_NOWEAPONDRIFT | SCA_FOVWARPS | SCA_ALIENSENSE, //int     abilities;
        WP_ALEVEL0, //weapon_t  startWeapon
        0.0f, //float   buildDist;
        140, //int     fov;
        0.0f, //float   bob;
        2.5f, //float   bobCycle;
        25, //int     steptime;
        LEVEL0_SPEED, //float   speed;
        10.0f, //float   acceleration;
        1.0f, //float   airAcceleration;
        6.0f, //float   friction;
        400.0f, //float   stopSpeed;
        250.0f, //float   jumpMagnitude;
        2.0f, //float   knockbackScale;
        { PCL_ALIEN_LEVEL1, PCL_NONE, PCL_NONE }, //int     children[ 3 ];
        LEVEL0_COST, //int     cost;
        LEVEL0_VALUE //int     value;
    },
    { PCL_ALIEN_LEVEL1, //int     classnum;
        "level1", //char    *classname;
        "Hydra", //char    *humanname;
        "spitter", //char    *modelname;
        0.6f, //float   modelScale;
        "default", //char    *skinname;
        1.0f, //float   shadowScale;
        "alien_general_hud", //char    *hudname;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        { -18, -18, -18 }, //vec3_t  mins;
        { 18, 18, 18 }, //vec3_t  maxs;
        { 18, 18, 18 }, //vec3_t  crouchmaxs;
        { -18, -18, -4 }, //vec3_t  deadmins;
        { 18, 18, 4 }, //vec3_t  deadmaxs;
        0.0f, //float   zOffset
        0.f, //float   spriteOffset;
        0, 0, //int     viewheight, crouchviewheight;
        LEVEL1_HEALTH, //int     health;
        0.0f, //float   fallDamage;
        LEVEL1_REGEN, //int     regenRate;
        SCA_NOWEAPONDRIFT | SCA_FOVWARPS | SCA_WALLCLIMBER | SCA_ALIENSENSE, //int     abilities;
        WP_ALEVEL1, //weapon_t  startWeapon
        0.0f, //float   buildDist;
        120, //int     fov;
        0.001f, //float   bob;
        1.8f, //float   bobCycle;
        60, //int     steptime;
        LEVEL1_SPEED, //float   speed;
        10.0f, //float   acceleration;
        1.0f, //float   airAcceleration;
        6.0f, //float   friction;
        300.0f, //float   stopSpeed;
        270.0f, //float   jumpMagnitude;
        1.2f, //float   knockbackScale;
        { PCL_ALIEN_LEVEL2, PCL_ALIEN_LEVEL1_UPG, PCL_NONE }, //int     children[ 3 ];
        LEVEL1_COST, //int     cost;
        LEVEL1_VALUE //int     value;
    },
    { PCL_ALIEN_LEVEL1_UPG, //int     classnum;
        "level1upg", //char    *classname;
        "Hydra Upgrade", //char    *humanname;
        "spitter", //char    *modelname;
        0.7f, //float   modelScale;
        "blue", //char    *skinname;
        1.0f, //float   shadowScale;
        "alien_general_hud", //char    *hudname;
        (1 << S2) | (1 << S3), //int  stages
        { -20, -20, -20 }, //vec3_t  mins;
        { 20, 20, 20 }, //vec3_t  maxs;
        { 20, 20, 20 }, //vec3_t  crouchmaxs;
        { -20, -20, -4 }, //vec3_t  deadmins;
        { 20, 20, 4 }, //vec3_t  deadmaxs;
        0.0f, //float   zOffset
        0.f, //float   spriteOffset;
        0, 0, //int     viewheight, crouchviewheight;
        LEVEL1_UPG_HEALTH, //int     health;
        0.0f, //float   fallDamage;
        LEVEL1_UPG_REGEN, //int     regenRate;
        SCA_NOWEAPONDRIFT | SCA_FOVWARPS | SCA_WALLCLIMBER | SCA_ALIENSENSE, //int     abilities;
        WP_ALEVEL1_UPG, //weapon_t  startWeapon
        0.0f, //float   buildDist;
        120, //int     fov;
        0.001f, //float   bob;
        1.8f, //float   bobCycle;
        60, //int     steptime;
        LEVEL1_UPG_SPEED, //float   speed;
        10.0f, //float   acceleration;
        1.0f, //float   airAcceleration;
        6.0f, //float   friction;
        300.0f, //float   stopSpeed;
        270.0f, //float   jumpMagnitude;
        1.1f, //float   knockbackScale;
        { PCL_ALIEN_LEVEL2, PCL_NONE, PCL_NONE }, //int     children[ 3 ];
        LEVEL1_UPG_COST, //int     cost;
        LEVEL1_UPG_VALUE //int     value;
    },
    { PCL_ALIEN_LEVEL2, //int     classnum;
        "level2", //char    *classname;
        "Chimera", //char    *humanname;
        "tarantula", //char    *modelname;
        0.75f, //float   modelScale;
        "default", //char    *skinname;
        1.0f, //float   shadowScale;
        "alien_general_hud", //char    *hudname;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        { -22, -22, -22 }, //vec3_t  mins;
        { 22, 22, 22 }, //vec3_t  maxs;
        { 22, 22, 22 }, //vec3_t  crouchmaxs;
        { -22, -22, -4 }, //vec3_t  deadmins;
        { 22, 22, 4 }, //vec3_t  deadmaxs;
        0.0f, //float   zOffset
        0.f, //float   spriteOffset;
        10, 10, //int     viewheight, crouchviewheight;
        LEVEL2_HEALTH, //int     health;
        0.0f, //float   fallDamage;
        LEVEL2_REGEN, //int     regenRate;
        SCA_NOWEAPONDRIFT | SCA_WALLJUMPER | SCA_FOVWARPS | SCA_ALIENSENSE, //int     abilities;
        WP_ALEVEL2, //weapon_t  startWeapon
        0.0f, //float   buildDist;
        90, //int     fov;
        0.001f, //float   bob;
        1.5f, //float   bobCycle;
        80, //int     steptime;
        LEVEL2_SPEED, //float   speed;
        10.0f, //float   acceleration;
        2.0f, //float   airAcceleration;
        6.0f, //float   friction;
        100.0f, //float   stopSpeed;
        400.0f, //float   jumpMagnitude;
        0.8f, //float   knockbackScale;
        { PCL_ALIEN_LEVEL3, PCL_ALIEN_LEVEL2_UPG, PCL_NONE }, //int     children[ 3 ];
        LEVEL2_COST, //int     cost;
        LEVEL2_VALUE //int     value;
    },
    { PCL_ALIEN_LEVEL2_UPG, //int     classnum;
        "level2upg", //char    *classname;
        "Chimera Upgrade", //char    *humanname;
        "tarantula", //char    *modelname;
        0.9f, //float   modelScale;
        "red", //char    *skinname;
        1.0f, //float   shadowScale;
        "alien_general_hud", //char    *hudname;
        (1 << S2) | (1 << S3), //int  stages
        { -24, -24, -24 }, //vec3_t  mins;
        { 24, 24, 24 }, //vec3_t  maxs;
        { 24, 24, 24 }, //vec3_t  crouchmaxs;
        { -24, -24, -4 }, //vec3_t  deadmins;
        { 24, 24, 4 }, //vec3_t  deadmaxs;
        0.0f, //float   zOffset
        0.f, //float   spriteOffset;
        12, 12, //int     viewheight, crouchviewheight;
        LEVEL2_UPG_HEALTH, //int     health;
        0.0f, //float   fallDamage;
        LEVEL2_UPG_REGEN, //int     regenRate;
        SCA_NOWEAPONDRIFT | SCA_WALLJUMPER | SCA_FOVWARPS | SCA_ALIENSENSE, //int     abilities;
        WP_ALEVEL2_UPG, //weapon_t  startWeapon
        0.0f, //float   buildDist;
        90, //int     fov;
        0.001f, //float   bob;
        1.5f, //float   bobCycle;
        80, //int     steptime;
        LEVEL2_UPG_SPEED, //float   speed;
        10.0f, //float   acceleration;
        2.0f, //float   airAcceleration;
        6.0f, //float   friction;
        100.0f, //float   stopSpeed;
        400.0f, //float   jumpMagnitude;
        0.7f, //float   knockbackScale;
        { PCL_ALIEN_LEVEL3, PCL_NONE, PCL_NONE }, //int     children[ 3 ];
        LEVEL2_UPG_COST, //int     cost;
        LEVEL2_UPG_VALUE //int     value;
    },
    { PCL_ALIEN_LEVEL3, //int     classnum;
        "level3", //char    *classname;
        "Dragoon", //char    *humanname;
        "prowl", //char    *modelname;
        1.0f, //float   modelScale;
        "default", //char    *skinname;
        1.0f, //float   shadowScale;
        "alien_general_hud", //char    *hudname;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        { -32, -32, -21 }, //vec3_t  mins;
        { 32, 32, 21 }, //vec3_t  maxs;
        { 32, 32, 21 }, //vec3_t  crouchmaxs;
        { -32, -32, -4 }, //vec3_t  deadmins;
        { 32, 32, 4 }, //vec3_t  deadmaxs;
        0.0f, //float   zOffset
        24.f, //float   spriteOffset;
        24, 24, //int     viewheight, crouchviewheight;
        LEVEL3_HEALTH, //int     health;
        0.0f, //float   fallDamage;
        LEVEL3_REGEN, //int     regenRate;
        SCA_NOWEAPONDRIFT | SCA_FOVWARPS | SCA_ALIENSENSE, //int     abilities;
        WP_ALEVEL3, //weapon_t  startWeapon
        0.0f, //float   buildDist;
        110, //int     fov;
        0.0005f, //float   bob;
        1.3f, //float   bobCycle;
        90, //int     steptime;
        LEVEL3_SPEED, //float   speed;
        10.0f, //float   acceleration;
        1.0f, //float   airAcceleration;
        6.0f, //float   friction;
        200.0f, //float   stopSpeed;
        270.0f, //float   jumpMagnitude;
        0.5f, //float   knockbackScale;
        { PCL_ALIEN_LEVEL4, PCL_ALIEN_LEVEL3_UPG, PCL_NONE }, //int     children[ 3 ];
        LEVEL3_COST, //int     cost;
        LEVEL3_VALUE //int     value;
    },
    { PCL_ALIEN_LEVEL3_UPG, //int     classnum;
        "level3upg", //char    *classname;
        "Dragoon Upgrade", //char    *humanname;
        "prowl", //char    *modelname;
        1.0f, //float   modelScale;
        "default", //char    *skinname;
        1.0f, //float   shadowScale;
        "alien_general_hud", //char    *hudname;
        (1 << S3), //int  stages
        { -32, -32, -21 }, //vec3_t  mins;
        { 32, 32, 21 }, //vec3_t  maxs;
        { 32, 32, 21 }, //vec3_t  crouchmaxs;
        { -32, -32, -4 }, //vec3_t  deadmins;
        { 32, 32, 4 }, //vec3_t  deadmaxs;
        0.0f, //float   zOffset
        32.f, //float   spriteOffset;
        27, 27, //int     viewheight, crouchviewheight;
        LEVEL3_UPG_HEALTH, //int     health;
        0.0f, //float   fallDamage;
        LEVEL3_UPG_REGEN, //int     regenRate;
        SCA_NOWEAPONDRIFT | SCA_FOVWARPS | SCA_ALIENSENSE, //int     abilities;
        WP_ALEVEL3_UPG, //weapon_t  startWeapon
        0.0f, //float   buildDist;
        110, //int     fov;
        0.0005f, //float   bob;
        1.3f, //float   bobCycle;
        90, //int     steptime;
        LEVEL3_UPG_SPEED, //float   speed;
        10.0f, //float   acceleration;
        1.0f, //float   airAcceleration;
        6.0f, //float   friction;
        200.0f, //float   stopSpeed;
        270.0f, //float   jumpMagnitude;
        0.4f, //float   knockbackScale;
        { PCL_ALIEN_LEVEL4, PCL_NONE, PCL_NONE }, //int     children[ 3 ];
        LEVEL3_UPG_COST, //int     cost;
        LEVEL3_UPG_VALUE //int     value;
    },
    { PCL_ALIEN_LEVEL4, //int     classnum;
        "level4", //char    *classname;
        "Big Mofo", //char    *humanname;
        "mofo", //char    *modelname;
        1.0f, //float   modelScale;
        "default", //char    *skinname;
        2.0f, //float   shadowScale;
        "alien_general_hud", //char    *hudname;
        (1 << S3), //int  stages
        { -30, -30, -20 }, //vec3_t  mins;
        { 30, 30, 20 }, //vec3_t  maxs;
        { 30, 30, 20 }, //vec3_t  crouchmaxs;
        { -15, -15, -4 }, //vec3_t  deadmins;
        { 15, 15, 4 }, //vec3_t  deadmaxs;
        0.0f, //float   zOffset
        88.f, //float   spriteOffset;
        35, 35, //int     viewheight, crouchviewheight;
        LEVEL4_HEALTH, //int     health;
        0.0f, //float   fallDamage;
        LEVEL4_REGEN, //int     regenRate;
        SCA_NOWEAPONDRIFT | SCA_FOVWARPS | SCA_ALIENSENSE, //int     abilities;
        WP_ALEVEL4, //weapon_t  startWeapon
        0.0f, //float   buildDist;
        90, //int     fov;
        0.001f, //float   bob;
        1.1f, //float   bobCycle;
        100, //int     steptime;
        LEVEL4_SPEED, //float   speed;
        10.0f, //float   acceleration;
        1.0f, //float   airAcceleration;
        6.0f, //float   friction;
        100.0f, //float   stopSpeed;
        170.0f, //float   jumpMagnitude;
        0.1f, //float   knockbackScale;
        { PCL_NONE, PCL_NONE, PCL_NONE }, //int     children[ 3 ];
        LEVEL4_COST, //int     cost;
        LEVEL4_VALUE //int     value;
    },
    { PCL_HUMAN, //int     classnum;
        "human_base", //char    *classname;
        "Human", //char    *humanname;
        "sarge", //char    *modelname;
        1.0f, //float   modelScale;
        "default", //char    *skinname;
        1.0f, //float   shadowScale;
        "human_hud", //char    *hudname;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        { -15, -15, -24 }, //vec3_t  mins;
        { 15, 15, 32 }, //vec3_t  maxs;
        { 15, 15, 16 }, //vec3_t  crouchmaxs;
        { -15, -15, -4 }, //vec3_t  deadmins;
        { 15, 15, 4 }, //vec3_t  deadmaxs;
        0.0f, //float   zOffset
        16.f, //float   spriteOffset;
        26, 12, //int     viewheight, crouchviewheight;
        100, //int     health;
        1.0f, //float   fallDamage;
        0, //int     regenRate;
        SCA_TAKESFALLDAMAGE | SCA_CANUSELADDERS, //int     abilities;
        WP_NONE, //special-cased in g_client.c          //weapon_t  startWeapon
        110.0f, //float   buildDist;
        90, //int     fov;
        0.002f, //float   bob;
        1.0f, //float   bobCycle;
        100, //int     steptime;
        1.0f, //float   speed;
        10.0f, //float   acceleration;
        1.0f, //float   airAcceleration;
        6.0f, //float   friction;
        100.0f, //float   stopSpeed;
        220.0f, //float   jumpMagnitude;
        1.0f, //float   knockbackScale;
        { PCL_NONE, PCL_NONE, PCL_NONE }, //int     children[ 3 ];
        0, //int     cost;
        0 //int     value;
    },
    { PCL_HUMAN_BSUIT, //int     classnum;
        "human_bsuit", //char    *classname;
        "bsuit", //char    *humanname;
        "keel", //char    *modelname;
        1.0f, //float   modelScale;
        "default", //char    *skinname;
        1.0f, //float   shadowScale;
        "human_hud", //char    *hudname;
        (1 << S3), //int  stages
        { -15, -15, -38 }, //vec3_t  mins;
        { 15, 15, 38 }, //vec3_t  maxs;
        { 15, 15, 38 }, //vec3_t  crouchmaxs;
        { -15, -15, -4 }, //vec3_t  deadmins;
        { 15, 15, 4 }, //vec3_t  deadmaxs;
        -16.0f, //float   zOffset
        25.f, //float   spriteOffset;
        35, 35, //int     viewheight, crouchviewheight;
        100, //int     health;
        1.0f, //float   fallDamage;
        0, //int     regenRate;
        SCA_TAKESFALLDAMAGE | SCA_CANUSELADDERS, //int     abilities;
        WP_NONE, //special-cased in g_client.c          //weapon_t  startWeapon
        110.0f, //float   buildDist;
        90, //int     fov;
        0.002f, //float   bob;
        1.0f, //float   bobCycle;
        100, //int     steptime;
        1.0f, //float   speed;
        10.0f, //float   acceleration;
        1.0f, //float   airAcceleration;
        6.0f, //float   friction;
        100.0f, //float   stopSpeed;
        270.0f, //float   jumpMagnitude;
        1.0f, //float   knockbackScale;
        { PCL_NONE, PCL_NONE, PCL_NONE }, //int     children[ 3 ];
        0, //int     cost;
        0 //int     value;
    }, };

int bg_numPclasses = sizeof(bg_classList) / sizeof(bg_classList[0]);

//separate from bg_classList to work around char struct init bug
classAttributeOverrides_t bg_classOverrideList[PCL_NUM_CLASSES];

/*
 ==============
 BG_FindClassNumForName
 ==============
 */
int
BG_FindClassNumForName(char *name)
{
  int i;

  for(i = 0;i < bg_numPclasses;i++)
  {
    if (!Q_stricmp(bg_classList[i].className, name))
      return bg_classList[i].classNum;
  }

  //wimp out
  return PCL_NONE;
}

/*
 ==============
 BG_FindNameForClassNum
 ==============
 */
char *
BG_FindNameForClassNum(int pclass)
{
  int i;

  for(i = 0;i < bg_numPclasses;i++)
  {
    if (bg_classList[i].classNum == pclass)
      return bg_classList[i].className;
  }

  Com_Printf(S_COLOR_YELLOW "WARNING: fallthrough in BG_FindNameForClassNum\n");
  //wimp out
  return 0;
}

/*
 ==============
 BG_FindHumanNameForClassNum
 ==============
 */
char *
BG_FindHumanNameForClassNum(int pclass)
{
  int i;

  if (bg_classOverrideList[pclass].humanName[0] != 0)
    return bg_classOverrideList[pclass].humanName;

  for(i = 0;i < bg_numPclasses;i++)
  {
    if (bg_classList[i].classNum == pclass)
      return bg_classList[i].humanName;
  }

  Com_Printf(S_COLOR_YELLOW "WARNING: fallthrough in BG_FindHumanNameForClassNum\n");
  //wimp out
  return 0;
}

/*
 ==============
 BG_FindModelNameForClass
 ==============
 */
char *
BG_FindModelNameForClass(int pclass)
{
  int i;

  if (bg_classOverrideList[pclass].modelName[0] != 0)
    return bg_classOverrideList[pclass].modelName;

  for(i = 0;i < bg_numPclasses;i++)
  {
    if (bg_classList[i].classNum == pclass)
      return bg_classList[i].modelName;
  }

  Com_Printf(S_COLOR_YELLOW "WARNING: fallthrough in BG_FindModelNameForClass\n");
  //note: must return a valid modelName!
  return bg_classList[0].modelName;
}

/*
 ==============
 BG_FindModelScaleForClass
 ==============
 */
float
BG_FindModelScaleForClass(int pclass)
{
  int i;

  if (bg_classOverrideList[pclass].modelScale != 0.0f)
    return bg_classOverrideList[pclass].modelScale;

  for(i = 0;i < bg_numPclasses;i++)
  {
    if (bg_classList[i].classNum == pclass)
    {
      return bg_classList[i].modelScale;
    }
  }

  Com_Printf(S_COLOR_YELLOW "WARNING: fallthrough in BG_FindModelScaleForClass( %d )\n", pclass);
  return 1.0f;
}

/*
 ==============
 BG_FindSkinNameForClass
 ==============
 */
char *
BG_FindSkinNameForClass(int pclass)
{
  int i;

  if (bg_classOverrideList[pclass].skinName[0] != 0)
    return bg_classOverrideList[pclass].skinName;

  for(i = 0;i < bg_numPclasses;i++)
  {
    if (bg_classList[i].classNum == pclass)
      return bg_classList[i].skinName;
  }

  Com_Printf(S_COLOR_YELLOW "WARNING: fallthrough in BG_FindSkinNameForClass\n");
  //note: must return a valid modelName!
  return bg_classList[0].skinName;
}

/*
 ==============
 BG_FindShadowScaleForClass
 ==============
 */
float
BG_FindShadowScaleForClass(int pclass)
{
  int i;

  if (bg_classOverrideList[pclass].shadowScale != 0.0f)
    return bg_classOverrideList[pclass].shadowScale;

  for(i = 0;i < bg_numPclasses;i++)
  {
    if (bg_classList[i].classNum == pclass)
    {
      return bg_classList[i].shadowScale;
    }
  }

  Com_Printf(S_COLOR_YELLOW "WARNING: fallthrough in BG_FindShadowScaleForClass( %d )\n", pclass);
  return 1.0f;
}

/*
 ==============
 BG_FindHudNameForClass
 ==============
 */
char *
BG_FindHudNameForClass(int pclass)
{
  int i;

  if (bg_classOverrideList[pclass].hudName[0] != 0)
    return bg_classOverrideList[pclass].hudName;

  for(i = 0;i < bg_numPclasses;i++)
  {
    if (bg_classList[i].classNum == pclass)
      return bg_classList[i].hudName;
  }

  Com_Printf(S_COLOR_YELLOW "WARNING: fallthrough in BG_FindHudNameForClass\n");
  //note: must return a valid hudName!
  return bg_classList[0].hudName;
}

/*
 ==============
 BG_FindStagesForClass
 ==============
 */
qboolean
BG_FindStagesForClass(int pclass, stage_t stage)
{
  int i;

  for(i = 0;i < bg_numPclasses;i++)
  {
    if (bg_classList[i].classNum == pclass)
    {
      if (bg_classList[i].stages & (1 << stage))
        return qtrue;
      else
        return qfalse;
    }
  }

  Com_Printf(S_COLOR_YELLOW "WARNING: fallthrough in BG_FindStagesForClass\n");
  return qfalse;
}

/*
 ==============
 BG_FindBBoxForClass
 ==============
 */
void
BG_FindBBoxForClass(int pclass, vec3_t mins, vec3_t maxs, vec3_t cmaxs, vec3_t dmins, vec3_t dmaxs)
{
  int i;

  for(i = 0;i < bg_numPclasses;i++)
  {
    if (bg_classList[i].classNum == pclass)
    {
      if (mins != NULL)
      {
        VectorCopy(bg_classList[ i ].mins, mins);

        if (VectorLength(bg_classOverrideList[pclass].mins))
          VectorCopy(bg_classOverrideList[ pclass ].mins, mins);
      }

      if (maxs != NULL)
      {
        VectorCopy(bg_classList[ i ].maxs, maxs);

        if (VectorLength(bg_classOverrideList[pclass].maxs))
          VectorCopy(bg_classOverrideList[ pclass ].maxs, maxs);
      }

      if (cmaxs != NULL)
      {
        VectorCopy(bg_classList[ i ].crouchMaxs, cmaxs);

        if (VectorLength(bg_classOverrideList[pclass].crouchMaxs))
          VectorCopy(bg_classOverrideList[ pclass ].crouchMaxs, cmaxs);
      }

      if (dmins != NULL)
      {
        VectorCopy(bg_classList[ i ].deadMins, dmins);

        if (VectorLength(bg_classOverrideList[pclass].deadMins))
          VectorCopy(bg_classOverrideList[ pclass ].deadMins, dmins);
      }

      if (dmaxs != NULL)
      {
        VectorCopy(bg_classList[ i ].deadMaxs, dmaxs);

        if (VectorLength(bg_classOverrideList[pclass].deadMaxs))
          VectorCopy(bg_classOverrideList[ pclass ].deadMaxs, dmaxs);
      }

      return;
    }
  }

  if (mins != NULL)
    VectorCopy(bg_classList[ 0 ].mins, mins);

  if (maxs != NULL)
    VectorCopy(bg_classList[ 0 ].maxs, maxs);

  if (cmaxs != NULL)
    VectorCopy(bg_classList[ 0 ].crouchMaxs, cmaxs);

  if (dmins != NULL)
    VectorCopy(bg_classList[ 0 ].deadMins, dmins);

  if (dmaxs != NULL)
    VectorCopy(bg_classList[ 0 ].deadMaxs, dmaxs);
}

/*
 BG_FindSpriteHeightForClass

 The height at which we can float a sprite above the client's head.
 We can't use the bounding box alone because they are not consistent with the
 models (!).
 ==============
 */

float
BG_FindSpriteHeightForClass(int pclass)
{
  int i;

  for(i = 0;i < bg_numPclasses;i++)
  {
    if (bg_classList[i].classNum == pclass)
    {
      return bg_classList[i].maxs[2] + bg_classList[i].spriteOffset;
    }
  }
  return 0.f;
}

/*
 ==============
 BG_FindZOffsetForClass
 ==============
 */
float
BG_FindZOffsetForClass(int pclass)
{
  int i;

  if (bg_classOverrideList[pclass].zOffset != 0.0f)
    return bg_classOverrideList[pclass].zOffset;

  for(i = 0;i < bg_numPclasses;i++)
  {
    if (bg_classList[i].classNum == pclass)
    {
      return bg_classList[i].zOffset;
    }
  }

  Com_Printf(S_COLOR_YELLOW "WARNING: fallthrough in BG_FindZOffsetForClass\n");
  return 0.0f;
}

/*
 ==============
 BG_FindViewheightForClass
 ==============
 */
void
BG_FindViewheightForClass(int pclass, int *viewheight, int *cViewheight)
{
  int i;
  int vh = 0;
  int cvh = 0;

  for(i = 0;i < bg_numPclasses;i++)
  {
    if (bg_classList[i].classNum == pclass)
    {
      vh = bg_classList[i].viewheight;
      cvh = bg_classList[i].crouchViewheight;
      break;
    }
  }

  if (bg_classOverrideList[pclass].viewheight != 0)
    vh = bg_classOverrideList[pclass].viewheight;
  if (bg_classOverrideList[pclass].crouchViewheight != 0)
    cvh = bg_classOverrideList[pclass].crouchViewheight;

  if (vh == 0)
    vh = bg_classList[0].viewheight;
  if (cvh == 0)
    cvh = bg_classList[0].crouchViewheight;

  if (viewheight != NULL)
    *viewheight = vh;
  if (cViewheight != NULL)
    *cViewheight = cvh;
}

/*
 ==============
 BG_FindHealthForClass
 ==============
 */
int
BG_FindHealthForClass(int pclass)
{
  int i;

  for(i = 0;i < bg_numPclasses;i++)
  {
    if (bg_classList[i].classNum == pclass)
    {
      return bg_classList[i].health;
    }
  }

  Com_Printf(S_COLOR_YELLOW "WARNING: fallthrough in BG_FindHealthForClass\n");
  return 100;
}

/*
 ==============
 BG_FindFallDamageForClass
 ==============
 */
float
BG_FindFallDamageForClass(int pclass)
{
  int i;

  for(i = 0;i < bg_numPclasses;i++)
  {
    if (bg_classList[i].classNum == pclass)
    {
      return bg_classList[i].fallDamage;
    }
  }

  Com_Printf(S_COLOR_YELLOW "WARNING: fallthrough in BG_FindFallDamageForClass\n");
  return 100;
}

/*
 ==============
 BG_FindRegenRateForClass
 ==============
 */
int
BG_FindRegenRateForClass(int pclass)
{
  int i;

  for(i = 0;i < bg_numPclasses;i++)
  {
    if (bg_classList[i].classNum == pclass)
    {
      return bg_classList[i].regenRate;
    }
  }

  Com_Printf(S_COLOR_YELLOW "WARNING: fallthrough in BG_FindRegenRateForClass\n");
  return 0;
}

/*
 ==============
 BG_FindFovForClass
 ==============
 */
int
BG_FindFovForClass(int pclass)
{
  int i;

  for(i = 0;i < bg_numPclasses;i++)
  {
    if (bg_classList[i].classNum == pclass)
    {
      return bg_classList[i].fov;
    }
  }

  Com_Printf(S_COLOR_YELLOW "WARNING: fallthrough in BG_FindFovForClass\n");
  return 90;
}

/*
 ==============
 BG_FindBobForClass
 ==============
 */
float
BG_FindBobForClass(int pclass)
{
  int i;

  for(i = 0;i < bg_numPclasses;i++)
  {
    if (bg_classList[i].classNum == pclass)
    {
      return bg_classList[i].bob;
    }
  }

  Com_Printf(S_COLOR_YELLOW "WARNING: fallthrough in BG_FindBobForClass\n");
  return 0.002;
}

/*
 ==============
 BG_FindBobCycleForClass
 ==============
 */
float
BG_FindBobCycleForClass(int pclass)
{
  int i;

  for(i = 0;i < bg_numPclasses;i++)
  {
    if (bg_classList[i].classNum == pclass)
    {
      return bg_classList[i].bobCycle;
    }
  }

  Com_Printf(S_COLOR_YELLOW "WARNING: fallthrough in BG_FindBobCycleForClass\n");
  return 1.0f;
}

/*
 ==============
 BG_FindSpeedForClass
 ==============
 */
float
BG_FindSpeedForClass(int pclass)
{
  int i;

  for(i = 0;i < bg_numPclasses;i++)
  {
    if (bg_classList[i].classNum == pclass)
    {
      return bg_classList[i].speed;
    }
  }

  Com_Printf(S_COLOR_YELLOW "WARNING: fallthrough in BG_FindSpeedForClass\n");
  return 1.0f;
}

/*
 ==============
 BG_FindAccelerationForClass
 ==============
 */
float
BG_FindAccelerationForClass(int pclass)
{
  int i;

  for(i = 0;i < bg_numPclasses;i++)
  {
    if (bg_classList[i].classNum == pclass)
    {
      return bg_classList[i].acceleration;
    }
  }

  Com_Printf(S_COLOR_YELLOW "WARNING: fallthrough in BG_FindAccelerationForClass\n");
  return 10.0f;
}

/*
 ==============
 BG_FindAirAccelerationForClass
 ==============
 */
float
BG_FindAirAccelerationForClass(int pclass)
{
  int i;

  for(i = 0;i < bg_numPclasses;i++)
  {
    if (bg_classList[i].classNum == pclass)
    {
      return bg_classList[i].airAcceleration;
    }
  }

  Com_Printf(S_COLOR_YELLOW "WARNING: fallthrough in BG_FindAirAccelerationForClass\n");
  return 1.0f;
}

/*
 ==============
 BG_FindFrictionForClass
 ==============
 */
float
BG_FindFrictionForClass(int pclass)
{
  int i;

  for(i = 0;i < bg_numPclasses;i++)
  {
    if (bg_classList[i].classNum == pclass)
    {
      return bg_classList[i].friction;
    }
  }

  Com_Printf(S_COLOR_YELLOW "WARNING: fallthrough in BG_FindFrictionForClass\n");
  return 6.0f;
}

/*
 ==============
 BG_FindStopSpeedForClass
 ==============
 */
float
BG_FindStopSpeedForClass(int pclass)
{
  int i;

  for(i = 0;i < bg_numPclasses;i++)
  {
    if (bg_classList[i].classNum == pclass)
    {
      return bg_classList[i].stopSpeed;
    }
  }

  Com_Printf(S_COLOR_YELLOW "WARNING: fallthrough in BG_FindStopSpeedForClass\n");
  return 100.0f;
}

/*
 ==============
 BG_FindJumpMagnitudeForClass
 ==============
 */
float
BG_FindJumpMagnitudeForClass(int pclass)
{
  int i;

  for(i = 0;i < bg_numPclasses;i++)
  {
    if (bg_classList[i].classNum == pclass)
    {
      return bg_classList[i].jumpMagnitude;
    }
  }

  Com_Printf(S_COLOR_YELLOW "WARNING: fallthrough in BG_FindJumpMagnitudeForClass\n");
  return 270.0f;
}

/*
 ==============
 BG_FindKnockbackScaleForClass
 ==============
 */
float
BG_FindKnockbackScaleForClass(int pclass)
{
  int i;

  for(i = 0;i < bg_numPclasses;i++)
  {
    if (bg_classList[i].classNum == pclass)
    {
      return bg_classList[i].knockbackScale;
    }
  }

  Com_Printf(S_COLOR_YELLOW "WARNING: fallthrough in BG_FindKnockbackScaleForClass\n");
  return 1.0f;
}

/*
 ==============
 BG_FindSteptimeForClass
 ==============
 */
int
BG_FindSteptimeForClass(int pclass)
{
  int i;

  for(i = 0;i < bg_numPclasses;i++)
  {
    if (bg_classList[i].classNum == pclass)
    {
      return bg_classList[i].steptime;
    }
  }

  Com_Printf(S_COLOR_YELLOW "WARNING: fallthrough in BG_FindSteptimeForClass\n");
  return 200;
}

/*
 ==============
 BG_ClassHasAbility
 ==============
 */
qboolean
BG_ClassHasAbility(int pclass, int ability)
{
  int i;

  for(i = 0;i < bg_numPclasses;i++)
  {
    if (bg_classList[i].classNum == pclass)
    {
      return (bg_classList[i].abilities & ability);
    }
  }

  return qfalse;
}

/*
 ==============
 BG_FindStartWeaponForClass
 ==============
 */
weapon_t
BG_FindStartWeaponForClass(int pclass)
{
  int i;

  for(i = 0;i < bg_numPclasses;i++)
  {
    if (bg_classList[i].classNum == pclass)
    {
      return bg_classList[i].startWeapon;
    }
  }

  Com_Printf(S_COLOR_YELLOW "WARNING: fallthrough in BG_FindStartWeaponForClass\n");
  return WP_NONE;
}

/*
 ==============
 BG_FindBuildDistForClass
 ==============
 */
float
BG_FindBuildDistForClass(int pclass)
{
  int i;

  for(i = 0;i < bg_numPclasses;i++)
  {
    if (bg_classList[i].classNum == pclass)
    {
      return bg_classList[i].buildDist;
    }
  }

  Com_Printf(S_COLOR_YELLOW "WARNING: fallthrough in BG_FindBuildDistForClass\n");
  return 0.0f;
}

/*
 ==============
 BG_ClassCanEvolveFromTo
 ==============
 */
int
BG_ClassCanEvolveFromTo(int fclass, int tclass, int credits, int num)
{
  int i, j, cost;

  cost = BG_FindCostOfClass(tclass);

  //base case
  if (credits < cost)
    return -1;

  if (fclass == PCL_NONE || tclass == PCL_NONE)
    return -1;

  for(i = 0;i < bg_numPclasses;i++)
  {
    if (bg_classList[i].classNum == fclass)
    {
      for(j = 0;j < 3;j++)
        if (bg_classList[i].children[j] == tclass)
          return num + cost;

      for(j = 0;j < 3;j++)
      {
        int sub;

        cost = BG_FindCostOfClass(bg_classList[i].children[j]);
        sub = BG_ClassCanEvolveFromTo(bg_classList[i].children[j], tclass, credits - cost, num
            + cost);
        if (sub >= 0)
          return sub;
      }

      return -1; //may as well return by this point
    }
  }

  return -1;
}

/*
 ==============
 BG_FindValueOfClass
 ==============
 */
int
BG_FindValueOfClass(int pclass)
{
  int i;

  for(i = 0;i < bg_numPclasses;i++)
  {
    if (bg_classList[i].classNum == pclass)
    {
      return bg_classList[i].value;
    }
  }

  Com_Printf(S_COLOR_YELLOW "WARNING: fallthrough in BG_FindValueOfClass\n");
  return 0;
}

/*
 ==============
 BG_FindCostOfClass
 ==============
 */
int
BG_FindCostOfClass(int pclass)
{
  int i;

  for(i = 0;i < bg_numPclasses;i++)
  {
    if (bg_classList[i].classNum == pclass)
    {
      return bg_classList[i].cost;
    }
  }

  Com_Printf(S_COLOR_YELLOW "WARNING: fallthrough in BG_FindCostOfClass\n");
  return 0;
}

/*
 ==============
 BG_FindOverrideForClass
 ==============
 */
static classAttributeOverrides_t *
BG_FindOverrideForClass(int pclass)
{
  return &bg_classOverrideList[pclass];
}

/*
 ======================
 BG_ParseClassFile

 Parses a configuration file describing a class
 ======================
 */
static qboolean
BG_ParseClassFile(const char *filename, classAttributeOverrides_t *cao)
{
  char *text_p;
  int i;
  int len;
  char *token;
  char text[20000];
  fileHandle_t f;
  float scale = 0.0f;

  // load the file
  len = trap_FS_FOpenFile(filename, &f, FS_READ);
  if (len < 0)
    return qfalse;

  if (len == 0 || len >= sizeof(text) - 1)
  {
    trap_FS_FCloseFile(f);
    Com_Printf(S_COLOR_RED "ERROR: Class file %s is %s\n", filename, len == 0 ? "empty"
        : "too long");
    return qfalse;
  }

  trap_FS_Read(text, len, f);
  text[len] = 0;
  trap_FS_FCloseFile(f);

  // parse the text
  text_p = text;

  // read optional parameters
  while(1)
  {
    token = COM_Parse(&text_p);

    if (!token)
      break;

    if (!Q_stricmp(token, ""))
      break;

    if (!Q_stricmp(token, "model"))
    {
      token = COM_Parse(&text_p);
      if (!token)
        break;

      Q_strncpyz(cao->modelName, token, sizeof(cao->modelName));

      continue;
    }
    else if (!Q_stricmp(token, "skin"))
    {
      token = COM_Parse(&text_p);
      if (!token)
        break;

      Q_strncpyz(cao->skinName, token, sizeof(cao->skinName));

      continue;
    }
    else if (!Q_stricmp(token, "hud"))
    {
      token = COM_Parse(&text_p);
      if (!token)
        break;

      Q_strncpyz(cao->hudName, token, sizeof(cao->hudName));

      continue;
    }
    else if (!Q_stricmp(token, "modelScale"))
    {
      token = COM_Parse(&text_p);
      if (!token)
        break;

      scale = atof(token);

      if (scale < 0.0f)
        scale = 0.0f;

      cao->modelScale = scale;

      continue;
    }
    else if (!Q_stricmp(token, "shadowScale"))
    {
      token = COM_Parse(&text_p);
      if (!token)
        break;

      scale = atof(token);

      if (scale < 0.0f)
        scale = 0.0f;

      cao->shadowScale = scale;

      continue;
    }
    else if (!Q_stricmp(token, "mins"))
    {
      for(i = 0;i <= 2;i++)
      {
        token = COM_Parse(&text_p);
        if (!token)
          break;

        cao->mins[i] = atof(token);
      }

      continue;
    }
    else if (!Q_stricmp(token, "maxs"))
    {
      for(i = 0;i <= 2;i++)
      {
        token = COM_Parse(&text_p);
        if (!token)
          break;

        cao->maxs[i] = atof(token);
      }

      continue;
    }
    else if (!Q_stricmp(token, "deadMins"))
    {
      for(i = 0;i <= 2;i++)
      {
        token = COM_Parse(&text_p);
        if (!token)
          break;

        cao->deadMins[i] = atof(token);
      }

      continue;
    }
    else if (!Q_stricmp(token, "deadMaxs"))
    {
      for(i = 0;i <= 2;i++)
      {
        token = COM_Parse(&text_p);
        if (!token)
          break;

        cao->deadMaxs[i] = atof(token);
      }

      continue;
    }
    else if (!Q_stricmp(token, "crouchMaxs"))
    {
      for(i = 0;i <= 2;i++)
      {
        token = COM_Parse(&text_p);
        if (!token)
          break;

        cao->crouchMaxs[i] = atof(token);
      }

      continue;
    }
    else if (!Q_stricmp(token, "viewheight"))
    {
      token = COM_Parse(&text_p);
      cao->viewheight = atoi(token);
      continue;
    }
    else if (!Q_stricmp(token, "crouchViewheight"))
    {
      token = COM_Parse(&text_p);
      cao->crouchViewheight = atoi(token);
      continue;
    }
    else if (!Q_stricmp(token, "zOffset"))
    {
      float offset;

      token = COM_Parse(&text_p);
      if (!token)
        break;

      offset = atof(token);

      cao->zOffset = offset;

      continue;
    }
    else if (!Q_stricmp(token, "name"))
    {
      token = COM_Parse(&text_p);
      if (!token)
        break;

      Q_strncpyz(cao->humanName, token, sizeof(cao->humanName));

      continue;
    }

    Com_Printf(S_COLOR_RED "ERROR: unknown token '%s'\n", token);
    return qfalse;
  }

  return qtrue;
}

/*
 ===============
 BG_InitClassOverrides

 Set any overrides specfied by file
 ===============
 */
void
BG_InitClassOverrides(void)
{
  int i;
  classAttributeOverrides_t *cao;

  for(i = PCL_NONE + 1;i < PCL_NUM_CLASSES;i++)
  {
    cao = BG_FindOverrideForClass(i);

    BG_ParseClassFile(va("overrides/classes/%s.cfg", BG_FindNameForClassNum(i)), cao);
  }
}

////////////////////////////////////////////////////////////////////////////////

weaponAttributes_t bg_weapons[] =
{
{ WP_MACHINEGUN, //int       weaponNum;
    RIFLE_PRICE, //int       price;
    (1 << S1) | (1 << S2) | (1 << S3), //int  stages
    SLOT_WEAPON, //int       slots;
    "machinegun", //char      *weaponName;
    "Tommy", //char      *weaponHumanName;
    RIFLE_CLIPSIZE, //int       maxAmmo;
    RIFLE_MAXCLIPS, //int       maxClips;
    qfalse, //int       infiniteAmmo;
    qfalse, //int       usesEnergy;
    RIFLE_REPEAT, //int       repeatRate1;
    0, //int       repeatRate2;
    0, //int       repeatRate3;
    RIFLE_RELOAD, //int       reloadTime;
    RIFLE_K_SCALE, //float     knockbackScale;
    qfalse, //qboolean  hasAltMode;
    qfalse, //qboolean  hasThirdMode;
    qtrue, //qboolean  canZoom;
    20.0f, //float     zoomFov;
    qtrue, //qboolean  purchasable;
    qtrue, //qboolean  longRanged;
    0, //int       buildDelay;
    WUT_HUMANS //WUTeam_t  team;
    },
    { WP_SHOTGUN, //int       weaponNum;
        SHOTGUN_PRICE, //int       price;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        SLOT_WEAPON, //int       slots;
        "shotty", //char      *weaponName;
        "Shotty", //char      *weaponHumanName;
        SHOTGUN_SHELLS, //int       maxAmmo;
        SHOTGUN_MAXCLIPS, //int       maxClips;
        qfalse, //int       infiniteAmmo;
        qfalse, //int       usesEnergy;
        SHOTGUN_REPEAT, //int       repeatRate1;
        0, //int       repeatRate2;
        0, //int       repeatRate3;
        SHOTGUN_RELOAD, //int       reloadTime;
        SHOTGUN_K_SCALE, //float     knockbackScale;
        qfalse, //qboolean  hasAltMode;
        qfalse, //qboolean  hasThirdMode;
        qtrue, //qboolean  canZoom;
        20.0f, //float     zoomFov;
        qtrue, //qboolean  purchasable;
        qtrue, //qboolean  longRanged;
        0, //int       buildDelay;
        WUT_HUMANS //WUTeam_t  team;
    },
    { WP_CHAINGUN, //int       weaponNum;
        CHAINGUN_PRICE, //int       price;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        SLOT_WEAPON, //int       slots;
        "chaingun", //char      *weaponName;
        "Chaingun", //char      *weaponHumanName;
        CHAINGUN_BULLETS, //int       maxAmmo;
        0, //int       maxClips;
        qfalse, //int       infiniteAmmo;
        qfalse, //int       usesEnergy;
        CHAINGUN_REPEAT, //int       repeatRate1;
        0, //int       repeatRate2;
        0, //int       repeatRate3;
        0, //int       reloadTime;
        CHAINGUN_K_SCALE, //float     knockbackScale;
        qfalse, //qboolean  hasAltMode;
        qfalse, //qboolean  hasThirdMode;
        qtrue, //qboolean  canZoom;
        20.0f, //float     zoomFov;
        qtrue, //qboolean  purchasable;
        qtrue, //qboolean  longRanged;
        0, //int       buildDelay;
        WUT_HUMANS //WUTeam_t  team;
    },
    { WP_MASS_DRIVER, //int       weaponNum;
        MDRIVER_PRICE, //int       price;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        SLOT_WEAPON, //int       slots;
        "mdriver", //char      *weaponName;
        "Mass Driver", //char      *weaponHumanName;
        MDRIVER_CLIPSIZE, //int       maxAmmo;
        MDRIVER_MAXCLIPS, //int       maxClips;
        qfalse, //int       infiniteAmmo;
        qtrue, //int       usesEnergy;
        MDRIVER_REPEAT, //int       repeatRate1;
        0, //int       repeatRate2;
        0, //int       repeatRate3;
        MDRIVER_RELOAD, //int       reloadTime;
        MDRIVER_K_SCALE, //float     knockbackScale;
        qfalse, //qboolean  hasAltMode;
        qfalse, //qboolean  hasThirdMode;
        qtrue, //qboolean  canZoom;
        15.0f, //float     zoomFov;
        qtrue, //qboolean  purchasable;
        qtrue, //qboolean  longRanged;
        0, //int       buildDelay;
        WUT_HUMANS //WUTeam_t  team;
    },
    { WP_LAS_GUN, //int       weaponNum;
        LASGUN_PRICE, //int       price;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        SLOT_WEAPON, //int       slots;
        "lasergun", //char      *weaponName;
        "Laser Gun", //char      *weaponHumanName;
        LASGUN_AMMO, //int       maxAmmo;
        0, //int       maxClips;
        qfalse, //int       infiniteAmmo;
        qtrue, //int       usesEnergy;
        LASGUN_REPEAT, //int       repeatRate1;
        0, //int       repeatRate2;
        0, //int       repeatRate3;
        LASGUN_RELOAD, //int       reloadTime;
        LASGUN_K_SCALE, //float     knockbackScale;
        qfalse, //qboolean  hasAltMode;
        qfalse, //qboolean  hasThirdMode;
        qtrue, //qboolean  canZoom;
        20.0f, //float     zoomFov;
        qtrue, //qboolean  purchasable;
        qtrue, //qboolean  longRanged;
        0, //int       buildDelay;
        WUT_HUMANS //WUTeam_t  team;
    },
    { WP_GRENADE, //int       weaponNum;
        GRENADE_PRICE, //int       price;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        SLOT_NONE, //int       slots;
        "grenade", //char      *weaponName;
        "Grenade", //char      *weaponHumanName;
        1, //int       maxAmmo;
        0, //int       maxClips;
        qfalse, //int       infiniteAmmo;
        qfalse, //int       usesEnergy;
        GRENADE_REPEAT, //int       repeatRate1;
        0, //int       repeatRate2;
        0, //int       repeatRate3;
        0, //int       reloadTime;
        GRENADE_K_SCALE, //float     knockbackScale;
        qfalse, //qboolean  hasAltMode;
        qfalse, //qboolean  hasThirdMode;
        qfalse, //qboolean  canZoom;
        90.0f, //float     zoomFov;
        qfalse, //qboolean  purchasable;
        qfalse, //qboolean  longRanged;
        0, //int       buildDelay;
        WUT_HUMANS //WUTeam_t  team;
    },
    { WP_MINE, //int       weaponNum;
        MINE_PRICE, //int       price;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        SLOT_NONE, //int       slots;
        "mine", //char      *weaponName;
        "Mine", //char      *weaponHumanName;
        1, //int       maxAmmo;
        0, //int       maxClips;
        qfalse, //int       infiniteAmmo;
        qfalse, //int       usesEnergy;
        MINE_REPEAT, //int       repeatRate1;
        0, //int       repeatRate2;
        0, //int       repeatRate3;
        0, //int       reloadTime;
        MINE_K_SCALE, //float     knockbackScale;
        qfalse, //qboolean  hasAltMode;
        qfalse, //qboolean  hasThirdMode;
        qfalse, //qboolean  canZoom;
        90.0f, //float     zoomFov;
        qfalse, //qboolean  purchasable;
        qfalse, //qboolean  longRanged;
        0, //int       buildDelay;
        WUT_HUMANS //WUTeam_t  team;
    },
    { WP_ZOMBIE_BITE, //int       weaponNum;
        GRENADE_PRICE, //int       price;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        SLOT_NONE, //int       slots;
        "zombie_bite", //char      *weaponName;
        "Zombie_bite", //char      *weaponHumanName;
        1, //int       maxAmmo;
        0, //int       maxClips;
        qtrue, //int       infiniteAmmo;
        qfalse, //int       usesEnergy;
        1000, //int       repeatRate1;
        0, //int       repeatRate2;
        0, //int       repeatRate3;
        0, //int       reloadTime;
        GRENADE_K_SCALE, //float     knockbackScale;
        qfalse, //qboolean  hasAltMode;
        qfalse, //qboolean  hasThirdMode;
        qfalse, //qboolean  canZoom;
        90.0f, //float     zoomFov;
        qfalse, //qboolean  purchasable;
        qfalse, //qboolean  longRanged;
        0, //int       buildDelay;
        WUT_HUMANS //WUTeam_t  team;
    },
    { WP_HBUILD, //int       weaponNum;
        HBUILD_PRICE, //int       price;
        (1 << S3), //int  stages
        SLOT_WEAPON, //int       slots;
        "ckit", //char      *weaponName;
        "Construction Kit", //char      *weaponHumanName;
        0, //int       maxAmmo;
        0, //int       maxClips;
        qtrue, //int       infiniteAmmo;
        qfalse, //int       usesEnergy;
        HBUILD_REPEAT, //int       repeatRate1;
        HBUILD_REPEAT, //int       repeatRate2;
        0, //int       repeatRate3;
        0, //int       reloadTime;
        0.0f, //float     knockbackScale;
        qtrue, //qboolean  hasAltMode;
        qfalse, //qboolean  hasThirdMode;
        qfalse, //qboolean  canZoom;
        90.0f, //float     zoomFov;
        qtrue, //qboolean  purchasable;
        qfalse, //qboolean  longRanged;
        HBUILD_DELAY, //int       buildDelay;
        WUT_NONE //WUTeam_t  team;
    },
    { WP_HBUILD2, //int       weaponNum;
        HBUILD2_PRICE, //int       price;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        0, //int       slots;
        "ackit", //char      *weaponName;
        "Adv Construction Kit", //char      *weaponHumanName;
        0, //int       maxAmmo;
        0, //int       maxClips;
        qtrue, //int       infiniteAmmo;
        qfalse, //int       usesEnergy;
        HBUILD2_REPEAT, //int       repeatRate1;
        HBUILD2_REPEAT, //int       repeatRate2;
        0, //int       repeatRate3;
        0, //int       reloadTime;
        0.0f, //float     knockbackScale;
        qtrue, //qboolean  hasAltMode;
        qfalse, //qboolean  hasThirdMode;
        qfalse, //qboolean  canZoom;
        90.0f, //float     zoomFov;
        qtrue, //qboolean  purchasable;
        qfalse, //qboolean  longRanged;
        HBUILD2_DELAY, //int       buildDelay;
        WUT_HUMANS //WUTeam_t  team;
    },
    { WP_ABUILD, //int       weaponNum;
        0, //int       price;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        SLOT_WEAPON, //int       slots;
        "abuild", //char      *weaponName;
        "Alien build weapon", //char      *weaponHumanName;
        0, //int       maxAmmo;
        0, //int       maxClips;
        qtrue, //int       infiniteAmmo;
        qfalse, //int       usesEnergy;
        ABUILDER_BUILD_REPEAT, //int       repeatRate1;
        ABUILDER_BUILD_REPEAT, //int       repeatRate2;
        0, //int       repeatRate3;
        0, //int       reloadTime;
        0.0f, //float     knockbackScale;
        qtrue, //qboolean  hasAltMode;
        qfalse, //qboolean  hasThirdMode;
        qfalse, //qboolean  canZoom;
        90.0f, //float     zoomFov;
        qtrue, //qboolean  purchasable;
        qfalse, //qboolean  longRanged;
        ABUILDER_BASE_DELAY, //int       buildDelay;
        WUT_ALIENS //WUTeam_t  team;
    },
    { WP_ABUILD2, //int       weaponNum;
        0, //int       price;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        SLOT_WEAPON, //int       slots;
        "abuildupg", //char      *weaponName;
        "Alien build weapon2", //char      *weaponHumanName;
        0, //int       maxAmmo;
        0, //int       maxClips;
        qtrue, //int       infiniteAmmo;
        qfalse, //int       usesEnergy;
        ABUILDER_BUILD_REPEAT, //int       repeatRate1;
        ABUILDER_CLAW_REPEAT, //int       repeatRate2;
        ABUILDER_BLOB_REPEAT, //int       repeatRate3;
        0, //int       reloadTime;
        ABUILDER_CLAW_K_SCALE, //float     knockbackScale;
        qtrue, //qboolean  hasAltMode;
        qtrue, //qboolean  hasThirdMode;
        qfalse, //qboolean  canZoom;
        90.0f, //float     zoomFov;
        qtrue, //qboolean  purchasable;
        qfalse, //qboolean  longRanged;
        ABUILDER_ADV_DELAY, //int       buildDelay;
        WUT_ALIENS //WUTeam_t  team;
    },
    { WP_ALEVEL0, //int       weaponNum;
        0, //int       price;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        SLOT_WEAPON, //int       slots;
        "level0", //char      *weaponName;
        "Bite", //char      *weaponHumanName;
        0, //int       maxAmmo;
        0, //int       maxClips;
        qtrue, //int       infiniteAmmo;
        qfalse, //int       usesEnergy;
        LEVEL0_BITE_REPEAT, //int       repeatRate1;
        0, //int       repeatRate2;
        0, //int       repeatRate3;
        0, //int       reloadTime;
        LEVEL0_BITE_K_SCALE, //float     knockbackScale;
        qfalse, //qboolean  hasAltMode;
        qfalse, //qboolean  hasThirdMode;
        qtrue, //qboolean  canZoom;
        20.0f, //float     zoomFov;
        qfalse, //qboolean  purchasable;
        qfalse, //qboolean  longRanged;
        0, //int       buildDelay;
        WUT_ALIENS //WUTeam_t  team;
    },
    { WP_ALEVEL1, //int       weaponNum;
        0, //int       price;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        SLOT_WEAPON, //int       slots;
        "level1", //char      *weaponName;
        "Claws", //char      *weaponHumanName;
        0, //int       maxAmmo;
        0, //int       maxClips;
        qtrue, //int       infiniteAmmo;
        qfalse, //int       usesEnergy;
        LEVEL1_CLAW_REPEAT, //int       repeatRate1;
        0, //int       repeatRate2;
        0, //int       repeatRate3;
        0, //int       reloadTime;
        LEVEL1_CLAW_K_SCALE, //float     knockbackScale;
        qfalse, //qboolean  hasAltMode;
        qfalse, //qboolean  hasThirdMode;
        qfalse, //qboolean  canZoom;
        90.0f, //float     zoomFov;
        qfalse, //qboolean  purchasable;
        qfalse, //qboolean  longRanged;
        0, //int       buildDelay;
        WUT_ALIENS //WUTeam_t  team;
    },
    { WP_ALEVEL1_UPG, //int       weaponNum;
        0, //int       price;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        SLOT_WEAPON, //int       slots;
        "level1upg", //char      *weaponName;
        "Claws Upgrade", //char      *weaponHumanName;
        0, //int       maxAmmo;
        0, //int       maxClips;
        qtrue, //int       infiniteAmmo;
        qfalse, //int       usesEnergy;
        LEVEL1_CLAW_U_REPEAT, //int       repeatRate1;
        LEVEL1_PCLOUD_REPEAT, //int       repeatRate2;
        0, //int       repeatRate3;
        0, //int       reloadTime;
        LEVEL1_CLAW_U_K_SCALE, //float     knockbackScale;
        qtrue, //qboolean  hasAltMode;
        qfalse, //qboolean  hasThirdMode;
        qfalse, //qboolean  canZoom;
        90.0f, //float     zoomFov;
        qfalse, //qboolean  purchasable;
        qtrue, //qboolean  longRanged;
        0, //int       buildDelay;
        WUT_ALIENS //WUTeam_t  team;
    },
    { WP_ALEVEL2, //int       weaponNum;
        0, //int       price;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        SLOT_WEAPON, //int       slots;
        "level2", //char      *weaponName;
        "Bite", //char      *weaponHumanName;
        0, //int       maxAmmo;
        0, //int       maxClips;
        qtrue, //int       infiniteAmmo;
        qfalse, //int       usesEnergy;
        LEVEL2_CLAW_REPEAT, //int       repeatRate1;
        0, //int       repeatRate2;
        0, //int       repeatRate3;
        0, //int       reloadTime;
        LEVEL2_CLAW_K_SCALE, //float     knockbackScale;
        qfalse, //qboolean  hasAltMode;
        qfalse, //qboolean  hasThirdMode;
        qfalse, //qboolean  canZoom;
        90.0f, //float     zoomFov;
        qfalse, //qboolean  purchasable;
        qfalse, //qboolean  longRanged;
        0, //int       buildDelay;
        WUT_ALIENS //WUTeam_t  team;
    },
    { WP_ALEVEL2_UPG, //int       weaponNum;
        0, //int       price;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        SLOT_WEAPON, //int       slots;
        "level2upg", //char      *weaponName;
        "Zap", //char      *weaponHumanName;
        0, //int       maxAmmo;
        0, //int       maxClips;
        qtrue, //int       infiniteAmmo;
        qfalse, //int       usesEnergy;
        LEVEL2_CLAW_U_REPEAT, //int       repeatRate1;
        LEVEL2_AREAZAP_REPEAT, //int       repeatRate2;
        0, //int       repeatRate3;
        0, //int       reloadTime;
        LEVEL2_CLAW_U_K_SCALE, //float     knockbackScale;
        qtrue, //qboolean  hasAltMode;
        qfalse, //qboolean  hasThirdMode;
        qfalse, //qboolean  canZoom;
        90.0f, //float     zoomFov;
        qfalse, //qboolean  purchasable;
        qfalse, //qboolean  longRanged;
        0, //int       buildDelay;
        WUT_ALIENS //WUTeam_t  team;
    },
    { WP_ALEVEL3, //int       weaponNum;
        0, //int       price;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        SLOT_WEAPON, //int       slots;
        "level3", //char      *weaponName;
        "Pounce", //char      *weaponHumanName;
        0, //int       maxAmmo;
        0, //int       maxClips;
        qtrue, //int       infiniteAmmo;
        qfalse, //int       usesEnergy;
        LEVEL3_CLAW_REPEAT, //int       repeatRate1;
        0, //int       repeatRate2;
        0, //int       repeatRate3;
        0, //int       reloadTime;
        LEVEL3_CLAW_K_SCALE, //float     knockbackScale;
        qfalse, //qboolean  hasAltMode;
        qfalse, //qboolean  hasThirdMode;
        qfalse, //qboolean  canZoom;
        90.0f, //float     zoomFov;
        qfalse, //qboolean  purchasable;
        qfalse, //qboolean  longRanged;
        0, //int       buildDelay;
        WUT_ALIENS //WUTeam_t  team;
    },
    { WP_ALEVEL3_UPG, //int       weaponNum;
        0, //int       price;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        SLOT_WEAPON, //int       slots;
        "level3upg", //char      *weaponName;
        "Pounce (upgrade)", //char      *weaponHumanName;
        3, //int       maxAmmo;
        0, //int       maxClips;
        qtrue, //int       infiniteAmmo;
        qfalse, //int       usesEnergy;
        LEVEL3_CLAW_U_REPEAT, //int       repeatRate1;
        0, //int       repeatRate2;
        LEVEL3_BOUNCEBALL_REPEAT, //int       repeatRate3;
        0, //int       reloadTime;
        LEVEL3_CLAW_U_K_SCALE, //float     knockbackScale;
        qfalse, //qboolean  hasAltMode;
        qtrue, //qboolean  hasThirdMode;
        qfalse, //qboolean  canZoom;
        90.0f, //float     zoomFov;
        qfalse, //qboolean  purchasable;
        qtrue, //qboolean  longRanged;
        0, //int       buildDelay;
        WUT_ALIENS //WUTeam_t  team;
    },
    { WP_ALEVEL4, //int       weaponNum;
        0, //int       price;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        SLOT_WEAPON, //int       slots;
        "level4", //char      *weaponName;
        "Charge", //char      *weaponHumanName;
        0, //int       maxAmmo;
        0, //int       maxClips;
        qtrue, //int       infiniteAmmo;
        qfalse, //int       usesEnergy;
        LEVEL4_CLAW_REPEAT, //int       repeatRate1;
        0, //int       repeatRate2;
        0, //int       repeatRate3;
        0, //int       reloadTime;
        LEVEL4_CLAW_K_SCALE, //float     knockbackScale;
        qfalse, //qboolean  hasAltMode;
        qfalse, //qboolean  hasThirdMode;
        qfalse, //qboolean  canZoom;
        90.0f, //float     zoomFov;
        qfalse, //qboolean  purchasable;
        qfalse, //qboolean  longRanged;
        0, //int       buildDelay;
        WUT_ALIENS //WUTeam_t  team;
    },
    { WP_LOCKBLOB_LAUNCHER, //int       weaponNum;
        0, //int       price;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        SLOT_WEAPON, //int       slots;
        "lockblob", //char      *weaponName;
        "Lock Blob", //char      *weaponHumanName;
        0, //int       maxAmmo;
        0, //int       maxClips;
        qtrue, //int       infiniteAmmo;
        qfalse, //int       usesEnergy;
        500, //int       repeatRate1;
        500, //int       repeatRate2;
        500, //int       repeatRate3;
        0, //int       reloadTime;
        LOCKBLOB_K_SCALE, //float     knockbackScale;
        qfalse, //qboolean  hasAltMode;
        qfalse, //qboolean  hasThirdMode;
        qfalse, //qboolean  canZoom;
        90.0f, //float     zoomFov;
        qfalse, //qboolean  purchasable;
        qfalse, //qboolean  longRanged;
        0, //int       buildDelay;
        WUT_ALIENS //WUTeam_t  team;
    },
    { WP_HIVE, //int       weaponNum;
        0, //int       price;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        SLOT_WEAPON, //int       slots;
        "hive", //char      *weaponName;
        "Hive", //char      *weaponHumanName;
        0, //int       maxAmmo;
        0, //int       maxClips;
        qtrue, //int       infiniteAmmo;
        qfalse, //int       usesEnergy;
        500, //int       repeatRate1;
        500, //int       repeatRate2;
        500, //int       repeatRate3;
        0, //int       reloadTime;
        HIVE_K_SCALE, //float     knockbackScale;
        qfalse, //qboolean  hasAltMode;
        qfalse, //qboolean  hasThirdMode;
        qfalse, //qboolean  canZoom;
        90.0f, //float     zoomFov;
        qfalse, //qboolean  purchasable;
        qfalse, //qboolean  longRanged;
        0, //int       buildDelay;
        WUT_ALIENS //WUTeam_t  team;
    },
    { WP_MGTURRET, //int       weaponNum;
        0, //int       price;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        SLOT_WEAPON, //int       slots;
        "mgturret", //char      *weaponName;
        "Machinegun Turret", //char      *weaponHumanName;
        0, //int       maxAmmo;
        0, //int       maxClips;
        qtrue, //int       infiniteAmmo;
        qfalse, //int       usesEnergy;
        0, //int       repeatRate1;
        0, //int       repeatRate2;
        0, //int       repeatRate3;
        0, //int       reloadTime;
        MGTURRET_K_SCALE, //float     knockbackScale;
        qfalse, //qboolean  hasAltMode;
        qfalse, //qboolean  hasThirdMode;
        qfalse, //qboolean  canZoom;
        90.0f, //float     zoomFov;
        qfalse, //qboolean  purchasable;
        qfalse, //qboolean  longRanged;
        0, //int       buildDelay;
        WUT_HUMANS //WUTeam_t  team;
    },
    { WP_TESLAGEN, //int       weaponNum;
        0, //int       price;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        SLOT_WEAPON, //int       slots;
        "teslagen", //char      *weaponName;
        "Tesla Generator", //char      *weaponHumanName;
        0, //int       maxAmmo;
        0, //int       maxClips;
        qtrue, //int       infiniteAmmo;
        qtrue, //int       usesEnergy;
        500, //int       repeatRate1;
        500, //int       repeatRate2;
        500, //int       repeatRate3;
        0, //int       reloadTime;
        TESLAGEN_K_SCALE, //float     knockbackScale;
        qfalse, //qboolean  hasAltMode;
        qfalse, //qboolean  hasThirdMode;
        qfalse, //qboolean  canZoom;
        90.0f, //float     zoomFov;
        qfalse, //qboolean  purchasable;
        qfalse, //qboolean  longRanged;
        0, //int       buildDelay;
        WUT_HUMANS //WUTeam_t  team;
    },
    { WP_AXE, //int       weaponNum;
        AXE_PRICE, //int       price;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        SLOT_WEAPON, //int       slots;
        "axe", //char      *weaponName;
        "Axe", //char      *weaponHumanName;
        0, //int       maxAmmo;
        0, //int       maxClips;
        qtrue, //int       infiniteAmmo;
        qfalse, //int       usesEnergy;
        AXE_REPEAT, //int       repeatRate1;
        AXE_REPEAT, //int       repeatRate2;
        0, //int       repeatRate3;
        0, //int       reloadTime;
        AXE_K_SCALE, //float     knockbackScale;
        qtrue, //qboolean  hasAltMode;
        qfalse, //qboolean  hasThirdMode;
        qfalse, //qboolean  canZoom;
        20.0f, //float     zoomFov;
        qtrue, //qboolean  purchasable;
        qtrue, //qboolean  longRanged;
        0, //int       buildDelay;
        WUT_HUMANS //WUTeam_t  team;
    },
    { WP_PISTOL, //int       weaponNum;
        0, //int       price;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        0, //int       slots;
        "pistol", //char      *weaponName;
        "Pistol", //char      *weaponHumanName;
        0, //int       maxAmmo;
        0, //int       maxClips;
        qtrue, //int       infiniteAmmo;
        qfalse, //int       usesEnergy;
        PISTOL_REPEAT, //int       repeatRate1;
        0, //int       repeatRate2;
        0, //int       repeatRate3;
        0, //int       reloadTime;
        PISTOL_K_SCALE, //float     knockbackScale;
        qfalse, //qboolean  hasAltMode;
        qfalse, //qboolean  hasThirdMode;
        qtrue, //qboolean  canZoom;
        20.0f, //float     zoomFov;
        qfalse, //qboolean  purchasable;
        qfalse, //qboolean  longRanged;
        0, //int       buildDelay;
        WUT_HUMANS //WUTeam_t  team;
    },
    { WP_ROCKET_LAUNCHER, //int       weaponNum;
        ROCKET_LAUNCHER_PRICE, //int       price;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        SLOT_WEAPON, //int       slots;
        "rocket_launcher", //char      *weaponName;
        "Rocket Launcher", //char      *weaponHumanName;
        ROCKET_LAUNCHER_AMMO, //int       maxAmmo;
        0, //int       maxClips;
        qfalse, //int       infiniteAmmo;
        qfalse, //int       usesEnergy;
        ROCKET_LAUNCHER_REPEAT, //int       repeatRate1;
        0, //int       repeatRate2;
        0, //int       repeatRate3;
        0, //int       reloadTime;
        ROCKET_LAUNCHER_K_SCALE, //float     knockbackScale;
        qfalse, //qboolean  hasAltMode;
        qfalse, //qboolean  hasThirdMode;
        qtrue, //qboolean  canZoom;
        20.0f, //float     zoomFov;
        qtrue, //qboolean  purchasable;
        qtrue, //qboolean  longRanged;
        0, //int       buildDelay;
        WUT_HUMANS //WUTeam_t  team;
    },
    { WP_LAUNCHER, //int       weaponNum;
        LAUNCHER_PRICE, //int       price;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        SLOT_WEAPON, //int       slots;
        "grenade_launcher", //char      *weaponName;
        "Grenade Launcher", //char      *weaponHumanName;
        LAUNCHER_AMMO, //int       maxAmmo;
        LAUNCHER_MAXAMMO, //int       maxClips;
        qfalse, //int       infiniteAmmo;
        qfalse, //int       usesEnergy;
        LAUNCHER_REPEAT, //int       repeatRate1;
        LAUNCHER_REPEAT, //int       repeatRate2;
        0, //int       repeatRate3;
        LAUNCHER_RELOAD, //int       reloadTime;
        LAUNCHER_K_SCALE, //float     knockbackScale;
        qtrue, //qboolean  hasAltMode;
        qfalse, //qboolean  hasThirdMode;
        qfalse, //qboolean  canZoom;
        90.0f, //float     zoomFov;
        qtrue, //qboolean  purchasable;
        qtrue, //qboolean  longRanged;
        0, //int       buildDelay;
        WUT_HUMANS //WUTeam_t  team;
    },
    { WP_DOME, //int       weaponNum;
        MINE_PRICE, //int       price;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        SLOT_NONE, //int       slots;
        "dome", //char      *weaponName;
        "Dome", //char      *weaponHumanName;
        1, //int       maxAmmo;
        0, //int       maxClips;
        qfalse, //int       infiniteAmmo;
        qfalse, //int       usesEnergy;
        MINE_REPEAT, //int       repeatRate1;
        0, //int       repeatRate2;
        0, //int       repeatRate3;
        0, //int       reloadTime;
        MINE_K_SCALE, //float     knockbackScale;
        qfalse, //qboolean  hasAltMode;
        qfalse, //qboolean  hasThirdMode;
        qfalse, //qboolean  canZoom;
        90.0f, //float     zoomFov;
        qfalse, //qboolean  purchasable;
        qfalse, //qboolean  longRanged;
        0, //int       buildDelay;
        WUT_HUMANS //WUTeam_t  team;
    }, };

int bg_numWeapons = sizeof(bg_weapons) / sizeof(bg_weapons[0]);

/*
 ==============
 BG_FindPriceForWeapon
 ==============
 */
int
BG_FindPriceForWeapon(int weapon)
{
  int i;

  for(i = 0;i < bg_numWeapons;i++)
  {
    if (bg_weapons[i].weaponNum == weapon)
    {
      return bg_weapons[i].price;
    }
  }

  return 100;
}

/*
 ==============
 BG_FindStagesForWeapon
 ==============
 */
qboolean
BG_FindStagesForWeapon(int weapon, stage_t stage)
{
  int i;

  for(i = 0;i < bg_numWeapons;i++)
  {
    if (bg_weapons[i].weaponNum == weapon)
    {
      if (bg_weapons[i].stages & (1 << stage))
        return qtrue;
      else
        return qfalse;
    }
  }

  return qfalse;
}

/*
 ==============
 BG_FindSlotsForWeapon
 ==============
 */
int
BG_FindSlotsForWeapon(int weapon)
{
  int i;

  for(i = 0;i < bg_numWeapons;i++)
  {
    if (bg_weapons[i].weaponNum == weapon)
    {
      return bg_weapons[i].slots;
    }
  }

  return SLOT_WEAPON;
}

/*
 ==============
 BG_FindNameForWeapon
 ==============
 */
char *
BG_FindNameForWeapon(int weapon)
{
  int i;

  for(i = 0;i < bg_numWeapons;i++)
  {
    if (bg_weapons[i].weaponNum == weapon)
      return bg_weapons[i].weaponName;
  }

  //wimp out
  return 0;
}

/*
 ==============
 BG_FindWeaponNumForName
 ==============
 */
int
BG_FindWeaponNumForName(char *name)
{
  int i;

  for(i = 0;i < bg_numWeapons;i++)
  {
    if (!Q_stricmp(bg_weapons[i].weaponName, name))
      return bg_weapons[i].weaponNum;
  }

  //wimp out
  return WP_NONE;
}

/*
 ==============
 BG_FindHumanNameForWeapon
 ==============
 */
char *
BG_FindHumanNameForWeapon(int weapon)
{
  int i;

  for(i = 0;i < bg_numWeapons;i++)
  {
    if (bg_weapons[i].weaponNum == weapon)
      return bg_weapons[i].weaponHumanName;
  }

  //wimp out
  return 0;
}

/*
 ==============
 BG_FindAmmoForWeapon
 ==============
 */
void
BG_FindAmmoForWeapon(int weapon, int *maxAmmo, int *maxClips)
{
  int i;

  for(i = 0;i < bg_numWeapons;i++)
  {
    if (bg_weapons[i].weaponNum == weapon)
    {
      if (maxAmmo != NULL)
        *maxAmmo = bg_weapons[i].maxAmmo;
      if (maxClips != NULL)
        *maxClips = bg_weapons[i].maxClips;

      //no need to keep going
      break;
    }
  }
}

/*
 ==============
 BG_FindInfinteAmmoForWeapon
 ==============
 */
qboolean
BG_FindInfinteAmmoForWeapon(int weapon)
{
  int i;

  for(i = 0;i < bg_numWeapons;i++)
  {
    if (bg_weapons[i].weaponNum == weapon)
    {
      return bg_weapons[i].infiniteAmmo;
    }
  }

  return qfalse;
}

/*
 ==============
 BG_FindUsesEnergyForWeapon
 ==============
 */
qboolean
BG_FindUsesEnergyForWeapon(int weapon)
{
  int i;

  for(i = 0;i < bg_numWeapons;i++)
  {
    if (bg_weapons[i].weaponNum == weapon)
    {
      return bg_weapons[i].usesEnergy;
    }
  }

  return qfalse;
}

/*
 ==============
 BG_FindRepeatRate1ForWeapon
 ==============
 */
int
BG_FindRepeatRate1ForWeapon(int weapon)
{
  int i;

  for(i = 0;i < bg_numWeapons;i++)
  {
    if (bg_weapons[i].weaponNum == weapon)
      return bg_weapons[i].repeatRate1;
  }

  return 1000;
}

/*
 ==============
 BG_FindRepeatRate2ForWeapon
 ==============
 */
int
BG_FindRepeatRate2ForWeapon(int weapon)
{
  int i;

  for(i = 0;i < bg_numWeapons;i++)
  {
    if (bg_weapons[i].weaponNum == weapon)
      return bg_weapons[i].repeatRate2;
  }

  return 1000;
}

/*
 ==============
 BG_FindRepeatRate3ForWeapon
 ==============
 */
int
BG_FindRepeatRate3ForWeapon(int weapon)
{
  int i;

  for(i = 0;i < bg_numWeapons;i++)
  {
    if (bg_weapons[i].weaponNum == weapon)
      return bg_weapons[i].repeatRate3;
  }

  return 1000;
}

/*
 ==============
 BG_FindReloadTimeForWeapon
 ==============
 */
int
BG_FindReloadTimeForWeapon(int weapon)
{
  int i;

  for(i = 0;i < bg_numWeapons;i++)
  {
    if (bg_weapons[i].weaponNum == weapon)
    {
      return bg_weapons[i].reloadTime;
    }
  }

  return 1000;
}

/*
 ==============
 BG_FindKnockbackScaleForWeapon
 ==============
 */
float
BG_FindKnockbackScaleForWeapon(int weapon)
{
  int i;

  for(i = 0;i < bg_numWeapons;i++)
  {
    if (bg_weapons[i].weaponNum == weapon)
    {
      return bg_weapons[i].knockbackScale;
    }
  }

  return 1.0f;
}

/*
 ==============
 BG_WeaponHasAltMode
 ==============
 */
qboolean
BG_WeaponHasAltMode(int weapon)
{
  int i;

  for(i = 0;i < bg_numWeapons;i++)
  {
    if (bg_weapons[i].weaponNum == weapon)
    {
      return bg_weapons[i].hasAltMode;
    }
  }

  return qfalse;
}

/*
 ==============
 BG_WeaponHasThirdMode
 ==============
 */
qboolean
BG_WeaponHasThirdMode(int weapon)
{
  int i;

  for(i = 0;i < bg_numWeapons;i++)
  {
    if (bg_weapons[i].weaponNum == weapon)
    {
      return bg_weapons[i].hasThirdMode;
    }
  }

  return qfalse;
}

/*
 ==============
 BG_WeaponCanZoom
 ==============
 */
qboolean
BG_WeaponCanZoom(int weapon)
{
  int i;

  for(i = 0;i < bg_numWeapons;i++)
  {
    if (bg_weapons[i].weaponNum == weapon)
    {
      return bg_weapons[i].canZoom;
    }
  }

  return qfalse;
}

/*
 ==============
 BG_FindZoomFovForWeapon
 ==============
 */
float
BG_FindZoomFovForWeapon(int weapon)
{
  int i;

  for(i = 0;i < bg_numWeapons;i++)
  {
    if (bg_weapons[i].weaponNum == weapon)
    {
      return bg_weapons[i].zoomFov;
    }
  }

  return qfalse;
}

/*
 ==============
 BG_FindPurchasableForWeapon
 ==============
 */
qboolean
BG_FindPurchasableForWeapon(int weapon)
{
  int i;

  for(i = 0;i < bg_numWeapons;i++)
  {
    if (bg_weapons[i].weaponNum == weapon)
    {
      return bg_weapons[i].purchasable;
    }
  }

  return qfalse;
}

/*
 ==============
 BG_FindLongRangeForWeapon
 ==============
 */
qboolean
BG_FindLongRangedForWeapon(int weapon)
{
  int i;

  for(i = 0;i < bg_numWeapons;i++)
  {
    if (bg_weapons[i].weaponNum == weapon)
    {
      return bg_weapons[i].longRanged;
    }
  }

  return qfalse;
}

/*
 ==============
 BG_FindBuildDelayForWeapon
 ==============
 */
int
BG_FindBuildDelayForWeapon(int weapon)
{
  int i;

  for(i = 0;i < bg_numWeapons;i++)
  {
    if (bg_weapons[i].weaponNum == weapon)
    {
      return bg_weapons[i].buildDelay;
    }
  }

  return 0;
}

/*
 ==============
 BG_FindTeamForWeapon
 ==============
 */
WUTeam_t
BG_FindTeamForWeapon(int weapon)
{
  int i;

  for(i = 0;i < bg_numWeapons;i++)
  {
    if (bg_weapons[i].weaponNum == weapon)
    {
      return bg_weapons[i].team;
    }
  }

  return WUT_NONE;
}

////////////////////////////////////////////////////////////////////////////////

upgradeAttributes_t bg_upgrades[] =
{
{ UP_LIGHTARMOUR, //int   upgradeNum;
    LIGHTARMOUR_PRICE, //int   price;
    (1 << S1) | (1 << S2) | (1 << S3), //int  stages
    SLOT_TORSO | SLOT_ARMS | SLOT_LEGS, //int   slots;
    "larmour", //char  *upgradeName;
    "Light Armour", //char  *upgradeHumanName;
    "icons/iconu_larmour", qtrue, //qboolean purchasable
    qfalse, //qboolean usable
    WUT_HUMANS //WUTeam_t  team;
    },
    { UP_HELMET, //int   upgradeNum;
        HELMET_PRICE, //int   price;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        SLOT_HEAD, //int   slots;
        "helmet", //char  *upgradeName;
        "Helmet", //char  *upgradeHumanName;
        "icons/iconu_helmet", qtrue, //qboolean purchasable
        qfalse, //qboolean usable
        WUT_HUMANS //WUTeam_t  team;
    },
    { UP_MEDKIT, //int   upgradeNum;
        MEDKIT_PRICE, //int   price;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        SLOT_NONE, //int   slots;
        "medkit", //char  *upgradeName;
        "Medkit", //char  *upgradeHumanName;
        "icons/iconu_atoxin", qfalse, //qboolean purchasable
        qtrue, //qboolean usable
        WUT_HUMANS //WUTeam_t  team;
    },
    { UP_BATTPACK, //int   upgradeNum;
        BATTPACK_PRICE, //int   price;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        SLOT_BACKPACK, //int   slots;
        "battpack", //char  *upgradeName;
        "Battery Pack", //char  *upgradeHumanName;
        "icons/iconu_battpack", qtrue, //qboolean purchasable
        qfalse, //qboolean usable
        WUT_HUMANS //WUTeam_t  team;
    },
    /*{ UP_JETPACK, //int   upgradeNum;
     JETPACK_PRICE, //int   price;
     (1 << S2) | (1 << S3), //int  stages
     SLOT_BACKPACK, //int   slots;
     "jetpack", //char  *upgradeName;
     "Jet Pack", //char  *upgradeHumanName;
     "icons/iconu_jetpack", qtrue, //qboolean purchasable
     qtrue, //qboolean usable
     WUT_HUMANS //WUTeam_t  team;
     },
     { UP_BATTLESUIT, //int   upgradeNum;
     BSUIT_PRICE, //int   price;
     (1 << S3), //int  stages
     SLOT_HEAD | SLOT_TORSO | SLOT_ARMS | SLOT_LEGS | SLOT_BACKPACK, //int   slots;
     "bsuit", //char  *upgradeName;
     "Battlesuit", //char  *upgradeHumanName;
     "icons/iconu_bsuit", qtrue, //qboolean purchasable
     qfalse, //qboolean usable
     WUT_HUMANS //WUTeam_t  team;
     },*/
    { UP_GRENADE, //int   upgradeNum;
        GRENADE_PRICE, //int   price;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        SLOT_NONE, //int   slots;
        "gren", //char  *upgradeName;
        "Grenade", //char  *upgradeHumanName;
        0, qtrue, //qboolean purchasable
        qtrue, //qboolean usable
        WUT_HUMANS //WUTeam_t  team;
    },
    { UP_MINE, //int   upgradeNum;
        MINE_PRICE, //int   price;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        SLOT_NONE, //int   slots;
        "min", //char  *upgradeName;
        "Mine", //char  *upgradeHumanName;
        0, qtrue, //qboolean purchasable
        qtrue, //qboolean usable
        WUT_HUMANS //WUTeam_t  team;
    },

    /*{
     UP_BOMB,             //int   upgradeNum;
     GRENADE_PRICE,          //int   price;
     ( 1 << S2 )|( 1 << S3 ),//int  stages
     SLOT_NONE,              //int   slots;
     "bom",                 //char  *upgradeName;
     "Bomb",              //char  *upgradeHumanName;
     0,
     qtrue,                  //qboolean purchasable
     qtrue,                  //qboolean usable
     WUT_HUMANS              //WUTeam_t  team;
     },*/
    { UP_AMMO, //int   upgradeNum;
        0, //int   price;
        (1 << S1) | (1 << S2) | (1 << S3), //int  stages
        SLOT_NONE, //int   slots;
        "ammo", //char  *upgradeName;
        "Ammunition", //char  *upgradeHumanName;
        0, qtrue, //qboolean purchasable
        qfalse, //qboolean usable
        WUT_HUMANS //WUTeam_t  team;
    } };

int bg_numUpgrades = sizeof(bg_upgrades) / sizeof(bg_upgrades[0]);

/*
 ==============
 BG_FindPriceForUpgrade
 ==============
 */
int
BG_FindPriceForUpgrade(int upgrade)
{
  int i;

  for(i = 0;i < bg_numUpgrades;i++)
  {
    if (bg_upgrades[i].upgradeNum == upgrade)
    {
      return bg_upgrades[i].price;
    }
  }

  return 100;
}

/*
 ==============
 BG_FindStagesForUpgrade
 ==============
 */
qboolean
BG_FindStagesForUpgrade(int upgrade, stage_t stage)
{
  int i;

  for(i = 0;i < bg_numUpgrades;i++)
  {
    if (bg_upgrades[i].upgradeNum == upgrade)
    {
      if (bg_upgrades[i].stages & (1 << stage))
        return qtrue;
      else
        return qfalse;
    }
  }

  return qfalse;
}

/*
 ==============
 BG_FindSlotsForUpgrade
 ==============
 */
int
BG_FindSlotsForUpgrade(int upgrade)
{
  int i;

  for(i = 0;i < bg_numUpgrades;i++)
  {
    if (bg_upgrades[i].upgradeNum == upgrade)
    {
      return bg_upgrades[i].slots;
    }
  }

  return SLOT_NONE;
}

/*
 ==============
 BG_FindNameForUpgrade
 ==============
 */
char *
BG_FindNameForUpgrade(int upgrade)
{
  int i;

  for(i = 0;i < bg_numUpgrades;i++)
  {
    if (bg_upgrades[i].upgradeNum == upgrade)
      return bg_upgrades[i].upgradeName;
  }

  //wimp out
  return 0;
}

/*
 ==============
 BG_FindUpgradeNumForName
 ==============
 */
int
BG_FindUpgradeNumForName(char *name)
{
  int i;

  for(i = 0;i < bg_numUpgrades;i++)
  {
    if (!Q_stricmp(bg_upgrades[i].upgradeName, name))
      return bg_upgrades[i].upgradeNum;
  }

  //wimp out
  return UP_NONE;
}

/*
 ==============
 BG_FindHumanNameForUpgrade
 ==============
 */
char *
BG_FindHumanNameForUpgrade(int upgrade)
{
  int i;

  for(i = 0;i < bg_numUpgrades;i++)
  {
    if (bg_upgrades[i].upgradeNum == upgrade)
      return bg_upgrades[i].upgradeHumanName;
  }

  //wimp out
  return 0;
}

/*
 ==============
 BG_FindIconForUpgrade
 ==============
 */
char *
BG_FindIconForUpgrade(int upgrade)
{
  int i;

  for(i = 0;i < bg_numUpgrades;i++)
  {
    if (bg_upgrades[i].upgradeNum == upgrade)
      return bg_upgrades[i].icon;
  }

  //wimp out
  return 0;
}

/*
 ==============
 BG_FindPurchasableForUpgrade
 ==============
 */
qboolean
BG_FindPurchasableForUpgrade(int upgrade)
{
  int i;

  for(i = 0;i < bg_numUpgrades;i++)
  {
    if (bg_upgrades[i].upgradeNum == upgrade)
      return bg_upgrades[i].purchasable;
  }

  return qfalse;
}

/*
 ==============
 BG_FindUsableForUpgrade
 ==============
 */
qboolean
BG_FindUsableForUpgrade(int upgrade)
{
  int i;

  for(i = 0;i < bg_numUpgrades;i++)
  {
    if (bg_upgrades[i].upgradeNum == upgrade)
      return bg_upgrades[i].usable;
  }

  return qfalse;
}

/*
 ==============
 BG_FindTeamForUpgrade
 ==============
 */
WUTeam_t
BG_FindTeamForUpgrade(int upgrade)
{
  int i;

  for(i = 0;i < bg_numUpgrades;i++)
  {
    if (bg_upgrades[i].upgradeNum == upgrade)
    {
      return bg_upgrades[i].team;
    }
  }

  return WUT_NONE;
}

////////////////////////////////////////////////////////////////////////////////

/*
 ================
 BG_EvaluateTrajectory

 ================
 */
void
BG_EvaluateTrajectory(const trajectory_t *tr, int atTime, vec3_t result)
{
  float deltaTime;
  float phase;

  switch(tr->trType)
  {
    case TR_STATIONARY:
    case TR_INTERPOLATE:
      VectorCopy(tr->trBase, result);
      break;

    case TR_LINEAR:
      deltaTime = (atTime - tr->trTime) * 0.001; // milliseconds to seconds
      VectorMA(tr->trBase, deltaTime, tr->trDelta, result);
      break;

    case TR_SINE:
      deltaTime = (atTime - tr->trTime) / (float) tr->trDuration;
      phase = sin(deltaTime * M_PI * 2);
      VectorMA(tr->trBase, phase, tr->trDelta, result);
      break;

    case TR_LINEAR_STOP:
      if (atTime > tr->trTime + tr->trDuration)
        atTime = tr->trTime + tr->trDuration;

      deltaTime = (atTime - tr->trTime) * 0.001; // milliseconds to seconds
      if (deltaTime < 0)
        deltaTime = 0;

      VectorMA(tr->trBase, deltaTime, tr->trDelta, result);
      break;

    case TR_GRAVITY:
      deltaTime = (atTime - tr->trTime) * 0.001; // milliseconds to seconds
      VectorMA(tr->trBase, deltaTime, tr->trDelta, result);
      result[2] -= 0.5 * DEFAULT_GRAVITY * deltaTime * deltaTime; // FIXME: local gravity...
      break;

    case TR_BUOYANCY:
      deltaTime = (atTime - tr->trTime) * 0.001; // milliseconds to seconds
      VectorMA(tr->trBase, deltaTime, tr->trDelta, result);
      result[2] += 0.5 * DEFAULT_GRAVITY * deltaTime * deltaTime; // FIXME: local gravity...
      break;

    default:
      Com_Error(ERR_DROP, "BG_EvaluateTrajectory: unknown trType: %i", tr->trTime);
      break;
  }
}

/*
 ================
 BG_EvaluateTrajectoryDelta

 For determining velocity at a given time
 ================
 */
void
BG_EvaluateTrajectoryDelta(const trajectory_t *tr, int atTime, vec3_t result)
{
  float deltaTime;
  float phase;

  switch(tr->trType)
  {
    case TR_STATIONARY:
    case TR_INTERPOLATE:
      VectorClear(result);
      break;

    case TR_LINEAR:
      VectorCopy(tr->trDelta, result);
      break;

    case TR_SINE:
      deltaTime = (atTime - tr->trTime) / (float) tr->trDuration;
      phase = cos(deltaTime * M_PI * 2); // derivative of sin = cos
      phase *= 0.5;
      VectorScale(tr->trDelta, phase, result);
      break;

    case TR_LINEAR_STOP:
      if (atTime > tr->trTime + tr->trDuration)
      {
        VectorClear(result);
        return;
      }
      VectorCopy(tr->trDelta, result);
      break;

    case TR_GRAVITY:
      deltaTime = (atTime - tr->trTime) * 0.001; // milliseconds to seconds
      VectorCopy(tr->trDelta, result);
      result[2] -= DEFAULT_GRAVITY * deltaTime; // FIXME: local gravity...
      break;

    case TR_BUOYANCY:
      deltaTime = (atTime - tr->trTime) * 0.001; // milliseconds to seconds
      VectorCopy(tr->trDelta, result);
      result[2] += DEFAULT_GRAVITY * deltaTime; // FIXME: local gravity...
      break;

    default:
      Com_Error(ERR_DROP, "BG_EvaluateTrajectoryDelta: unknown trType: %i", tr->trTime);
      break;
  }
}

char *eventnames[] =
{ "EV_NONE",

"EV_FOOTSTEP", "EV_FOOTSTEP_METAL", "EV_FOOTSTEP_SQUELCH", "EV_FOOTSPLASH", "EV_FOOTWADE",
    "EV_SWIM",

    "EV_STEP_4", "EV_STEP_8", "EV_STEP_12", "EV_STEP_16",

    "EV_STEPDN_4", "EV_STEPDN_8", "EV_STEPDN_12", "EV_STEPDN_16",

    "EV_FALL_SHORT", "EV_FALL_MEDIUM", "EV_FALL_FAR", "EV_FALLING",

    "EV_JUMP", "EV_WATER_TOUCH", // foot touches
    "EV_WATER_LEAVE", // foot leaves
    "EV_WATER_UNDER", // head touches
    "EV_WATER_CLEAR", // head leaves

    "EV_NOAMMO", "EV_CHANGE_WEAPON", "EV_FIRE_WEAPON", "EV_FIRE_WEAPON2", "EV_FIRE_WEAPON3",

    "EV_PLAYER_RESPAWN", //TA: for fovwarp effects
    "EV_PLAYER_TELEPORT_IN", "EV_PLAYER_TELEPORT_OUT",

    "EV_GRENADE_BOUNCE", // eventParm will be the soundindex

    "EV_GENERAL_SOUND", "EV_GLOBAL_SOUND", // no attenuation

    "EV_BULLET_HIT_FLESH", "EV_BULLET_HIT_WALL",

    "EV_SHOTGUN",

    "EV_MISSILE_HIT", "EV_MISSILE_MISS", "EV_MISSILE_MISS_METAL", "EV_TESLATRAIL", "EV_BULLET", // otherEntity is the shooter

    "EV_LEV1_GRAB", "EV_LEV4_CHARGE_PREPARE", "EV_LEV4_CHARGE_START",

    "EV_PAIN", "EV_DEATH1", "EV_DEATH2", "EV_DEATH3", "EV_OBITUARY",

    "EV_GIB_PLAYER", // gib a previously living player

    "EV_BUILD_CONSTRUCT", //TA
    "EV_BUILD_DESTROY", //TA
    "EV_BUILD_DELAY", //TA: can't build yet
    "EV_BUILD_REPAIR", //TA: repairing buildable
    "EV_BUILD_REPAIRED", //TA: buildable has full health
    "EV_HUMAN_BUILDABLE_EXPLOSION", "EV_ALIEN_BUILDABLE_EXPLOSION", "EV_ALIEN_ACIDTUBE",

    "EV_MEDKIT_USED",

    "EV_ALIEN_EVOLVE", "EV_ALIEN_EVOLVE_FAILED",

    "EV_DEBUG_LINE", "EV_STOPLOOPINGSOUND", "EV_TAUNT",

    "EV_OVERMIND_ATTACK", //TA: overmind under attack
    "EV_OVERMIND_DYING", //TA: overmind close to death
    "EV_OVERMIND_SPAWNS", //TA: overmind needs spawns

    "EV_DCC_ATTACK", //TA: dcc under attack

    "EV_RPTUSE_SOUND" //TA: trigger a sound
    };

/*
 ===============
 BG_AddPredictableEventToPlayerstate

 Handles the sequence numbers
 ===============
 */

void
trap_Cvar_VariableStringBuffer(const char *var_name, char *buffer, int bufsize);

void
BG_AddPredictableEventToPlayerstate(int newEvent, int eventParm, playerState_t *ps)
{
#ifdef _DEBUG
  {
    char buf[ 256 ];
    trap_Cvar_VariableStringBuffer("showevents", buf, sizeof ( buf));

    if (atof(buf) != 0)
    {
#ifdef QAGAME
      Com_Printf(" game event svt %5d -> %5d: num = %20s parm %d\n",
          ps->pmove_framecount/*ps->commandTime*/, ps->eventSequence, eventnames[ newEvent ], eventParm);
#else
      Com_Printf("Cgame event svt %5d -> %5d: num = %20s parm %d\n",
          ps->pmove_framecount/*ps->commandTime*/, ps->eventSequence, eventnames[ newEvent ], eventParm);
#endif
    }
  }
#endif
  ps->events[ps->eventSequence & (MAX_PS_EVENTS - 1)] = newEvent;
  ps->eventParms[ps->eventSequence & (MAX_PS_EVENTS - 1)] = eventParm;
  ps->eventSequence++;
}

/*
 ========================
 BG_PlayerStateToEntityState

 This is done after each set of usercmd_t on the server,
 and after local prediction on the client
 ========================
 */
void
BG_PlayerStateToEntityState(playerState_t *ps, entityState_t *s, qboolean snap)
{
  int i;

  if (ps->pm_type == PM_INTERMISSION || ps->pm_type == PM_SPECTATOR || ps->pm_type == PM_FREEZE)
    s->eType = ET_INVISIBLE;
  else if (ps->persistant[PERS_TEAM] == TEAM_SPECTATOR)
    s->eType = ET_INVISIBLE;
  else
    s->eType = ET_PLAYER;

  s->number = ps->clientNum;

  s->pos.trType = TR_INTERPOLATE;
  VectorCopy(ps->origin, s->pos.trBase);

  if (snap)
    SnapVector(s->pos.trBase);

  //set the trDelta for flag direction
  VectorCopy(ps->velocity, s->pos.trDelta);

  s->apos.trType = TR_INTERPOLATE;
  VectorCopy(ps->viewangles, s->apos.trBase);

  if (snap)
    SnapVector(s->apos.trBase);

  //TA: i need for other things :)
  //s->angles2[YAW] = ps->movementDir;
  s->time2 = ps->movementDir;
  s->legsAnim = ps->legsAnim;
  s->torsoAnim = ps->torsoAnim;
  s->clientNum = ps->clientNum; // ET_PLAYER looks here instead of at number
  // so corpses can also reference the proper config
  s->eFlags = ps->eFlags;
  if (ps->stats[STAT_HEALTH] <= 0)
    s->eFlags |= EF_DEAD;
  else
    s->eFlags &= ~EF_DEAD;

  if (ps->stats[STAT_STATE] & SS_BLOBLOCKED)
    s->eFlags |= EF_BLOBLOCKED;
  else
    s->eFlags &= ~EF_BLOBLOCKED;

  if (ps->stats[STAT_STATE] & SS_ONFIRE)
    s->eFlags |= EF_ONFIRE;
  else
    s->eFlags &= ~EF_ONFIRE;

  if (ps->externalEvent)
  {
    s->event = ps->externalEvent;
    s->eventParm = ps->externalEventParm;
  }
  else if (ps->entityEventSequence < ps->eventSequence)
  {
    int seq;

    if (ps->entityEventSequence < ps->eventSequence - MAX_PS_EVENTS)
      ps->entityEventSequence = ps->eventSequence - MAX_PS_EVENTS;

    seq = ps->entityEventSequence & (MAX_PS_EVENTS - 1);
    s->event = ps->events[seq] | ((ps->entityEventSequence & 3) << 8);
    s->eventParm = ps->eventParms[seq];
    ps->entityEventSequence++;
  }

  s->weapon = ps->weapon;
  s->groundEntityNum = ps->groundEntityNum;

  //store items held and active items in modelindex and modelindex2
  s->modelindex = 0;
  s->modelindex2 = 0;
  for(i = UP_NONE + 1;i < UP_NUM_UPGRADES;i++)
  {
    if (BG_InventoryContainsUpgrade(i, ps->stats))
    {
      s->modelindex |= 1 << i;

      if (BG_UpgradeIsActive(i, ps->stats))
        s->modelindex2 |= 1 << i;
    }
  }

  //TA: use powerups field to store team/class info:
  s->powerups = ps->stats[STAT_PTEAM] | (ps->stats[STAT_PCLASS] << 8);

  //TA: have to get the surfNormal thru somehow...
  VectorCopy(ps->grapplePoint, s->angles2);
  if (ps->stats[STAT_STATE] & SS_WALLCLIMBINGCEILING)
    s->eFlags |= EF_WALLCLIMBCEILING;

  s->loopSound = ps->loopSound;
  s->generic1 = ps->generic1;

  if (s->generic1 <= WPM_NONE || s->generic1 >= WPM_NUM_WEAPONMODES)
    s->generic1 = WPM_PRIMARY;

  s->otherEntityNum = ps->otherEntityNum;
}

/*
 ========================
 BG_PlayerStateToEntityStateExtraPolate

 This is done after each set of usercmd_t on the server,
 and after local prediction on the client
 ========================
 */
void
BG_PlayerStateToEntityStateExtraPolate(playerState_t *ps, entityState_t *s, int time, qboolean snap)
{
  int i;

  if (ps->pm_type == PM_INTERMISSION || ps->pm_type == PM_SPECTATOR || ps->pm_type == PM_FREEZE)
    s->eType = ET_INVISIBLE;
  else if (ps->persistant[PERS_TEAM] == TEAM_SPECTATOR)
    s->eType = ET_INVISIBLE;
  else
    s->eType = ET_PLAYER;

  s->number = ps->clientNum;

  s->pos.trType = TR_LINEAR_STOP;
  VectorCopy(ps->origin, s->pos.trBase);

  if (snap)
    SnapVector(s->pos.trBase);

  // set the trDelta for flag direction and linear prediction
  VectorCopy(ps->velocity, s->pos.trDelta);
  // set the time for linear prediction
  s->pos.trTime = time;
  // set maximum extra polation time
  s->pos.trDuration = 50; // 1000 / sv_fps (default = 20)

  s->apos.trType = TR_INTERPOLATE;
  VectorCopy(ps->viewangles, s->apos.trBase);
  if (snap)
    SnapVector(s->apos.trBase);

  //TA: i need for other things :)
  //s->angles2[YAW] = ps->movementDir;
  s->time2 = ps->movementDir;
  s->legsAnim = ps->legsAnim;
  s->torsoAnim = ps->torsoAnim;
  s->clientNum = ps->clientNum; // ET_PLAYER looks here instead of at number
  // so corpses can also reference the proper config
  s->eFlags = ps->eFlags;

  if (ps->stats[STAT_HEALTH] <= 0)
    s->eFlags |= EF_DEAD;
  else
    s->eFlags &= ~EF_DEAD;

  if (ps->stats[STAT_STATE] & SS_BLOBLOCKED)
    s->eFlags |= EF_BLOBLOCKED;
  else
    s->eFlags &= ~EF_BLOBLOCKED;

  if (ps->stats[STAT_STATE] & SS_ONFIRE)
    s->eFlags |= EF_ONFIRE;
  else
    s->eFlags &= ~EF_ONFIRE;

  if (ps->externalEvent)
  {
    s->event = ps->externalEvent;
    s->eventParm = ps->externalEventParm;
  }
  else if (ps->entityEventSequence < ps->eventSequence)
  {
    int seq;

    if (ps->entityEventSequence < ps->eventSequence - MAX_PS_EVENTS)
      ps->entityEventSequence = ps->eventSequence - MAX_PS_EVENTS;

    seq = ps->entityEventSequence & (MAX_PS_EVENTS - 1);
    s->event = ps->events[seq] | ((ps->entityEventSequence & 3) << 8);
    s->eventParm = ps->eventParms[seq];
    ps->entityEventSequence++;
  }

  s->weapon = ps->weapon;
  s->groundEntityNum = ps->groundEntityNum;

  //store items held and active items in modelindex and modelindex2
  s->modelindex = 0;
  s->modelindex2 = 0;

  for(i = UP_NONE + 1;i < UP_NUM_UPGRADES;i++)
  {
    if (BG_InventoryContainsUpgrade(i, ps->stats))
    {
      s->modelindex |= 1 << i;

      if (BG_UpgradeIsActive(i, ps->stats))
        s->modelindex2 |= 1 << i;
    }
  }

  //TA: use powerups field to store team/class info:
  s->powerups = ps->stats[STAT_PTEAM] | (ps->stats[STAT_PCLASS] << 8);

  //TA: have to get the surfNormal thru somehow...
  VectorCopy(ps->grapplePoint, s->angles2);
  if (ps->stats[STAT_STATE] & SS_WALLCLIMBINGCEILING)
    s->eFlags |= EF_WALLCLIMBCEILING;

  s->loopSound = ps->loopSound;
  s->generic1 = ps->generic1;

  if (s->generic1 <= WPM_NONE || s->generic1 >= WPM_NUM_WEAPONMODES)
    s->generic1 = WPM_PRIMARY;

  s->otherEntityNum = ps->otherEntityNum;
}

/*
 ========================
 BG_UnpackAmmoArray

 Extract the ammo quantity from the array
 ========================
 */
void
BG_UnpackAmmoArray(int weapon, int psAmmo[], int psAmmo2[], int *ammo, int *clips)
{
  int ammoarray[32];
  int i;

  for(i = 0;i <= 15;i++)
    ammoarray[i] = psAmmo[i];

  for(i = 16;i <= 31;i++)
    ammoarray[i] = psAmmo2[i - 16];

  if (ammo != NULL)
    *ammo = ammoarray[weapon] & 0x0FFF;

  if (clips != NULL)
    *clips = (ammoarray[weapon] >> 12) & 0x0F;
}

/*
 ========================
 BG_PackAmmoArray

 Pack the ammo quantity into the array
 ========================
 */
void
BG_PackAmmoArray(int weapon, int psAmmo[], int psAmmo2[], int ammo, int clips)
{
  int weaponvalue;

  weaponvalue = ammo | (clips << 12);

  if (weapon <= 15)
    psAmmo[weapon] = weaponvalue;
  else if (weapon >= 16)
    psAmmo2[weapon - 16] = weaponvalue;
}

/*
 ========================
 BG_WeaponIsFull

 Check if a weapon has full ammo
 ========================
 */
qboolean
BG_WeaponIsFull(weapon_t weapon, int stats[], int psAmmo[], int psAmmo2[])
{
  int maxAmmo, maxClips;
  int ammo, clips;

  BG_FindAmmoForWeapon(weapon, &maxAmmo, &maxClips);
  BG_UnpackAmmoArray(weapon, psAmmo, psAmmo2, &ammo, &clips);

  if (BG_InventoryContainsUpgrade(UP_BATTPACK, stats))
    maxAmmo = (int) ((float) maxAmmo * BATTPACK_MODIFIER);

  return (maxAmmo == ammo) && (maxClips == clips);
}

/*
 ========================
 BG_AddWeaponToInventory

 Give a player a weapon
 ========================
 */
void
BG_AddWeaponToInventory(int weapon, int stats[])
{
  int weaponList;

  weaponList = (stats[STAT_WEAPONS] & 0x0000FFFF) | ((stats[STAT_WEAPONS2] << 16) & 0xFFFF0000);

  weaponList |= (1 << weapon);

  stats[STAT_WEAPONS] = weaponList & 0x0000FFFF;
  stats[STAT_WEAPONS2] = (weaponList & 0xFFFF0000) >> 16;

  if (stats[STAT_SLOTS] & BG_FindSlotsForWeapon(weapon))
    Com_Printf(S_COLOR_YELLOW "WARNING: Held items conflict with weapon %d\n", weapon);

  stats[STAT_SLOTS] |= BG_FindSlotsForWeapon(weapon);
}

/*
 ========================
 BG_RemoveWeaponToInventory

 Take a weapon from a player
 ========================
 */
void
BG_RemoveWeaponFromInventory(int weapon, int stats[])
{
  int weaponList;

  weaponList = (stats[STAT_WEAPONS] & 0x0000FFFF) | ((stats[STAT_WEAPONS2] << 16) & 0xFFFF0000);

  weaponList &= ~(1 << weapon);

  stats[STAT_WEAPONS] = weaponList & 0x0000FFFF;
  stats[STAT_WEAPONS2] = (weaponList & 0xFFFF0000) >> 16;

  stats[STAT_SLOTS] &= ~BG_FindSlotsForWeapon(weapon);
}

/*
 ========================
 BG_InventoryContainsWeapon

 Does the player hold a weapon?
 ========================
 */
qboolean
BG_InventoryContainsWeapon(int weapon, int stats[])
{
  int weaponList;

  weaponList = (stats[STAT_WEAPONS] & 0x0000FFFF) | ((stats[STAT_WEAPONS2] << 16) & 0xFFFF0000);

  return (weaponList & (1 << weapon));
}

/*
 ========================
 BG_AddUpgradeToInventory

 Give the player an upgrade
 ========================
 */
void
BG_AddUpgradeToInventory(int item, int stats[])
{
  stats[STAT_ITEMS] |= (1 << item);

  //if (stats[ STAT_SLOTS ] & BG_FindSlotsForUpgrade(item))
  //  Com_Printf(S_COLOR_YELLOW "WARNING: Held items conflict with upgrade %d\n", item);

  stats[STAT_SLOTS] |= BG_FindSlotsForUpgrade(item);
}

/*
 ========================
 BG_RemoveUpgradeFromInventory

 Take an upgrade from the player
 ========================
 */
void
BG_RemoveUpgradeFromInventory(int item, int stats[])
{
  stats[STAT_ITEMS] &= ~(1 << item);

  stats[STAT_SLOTS] &= ~BG_FindSlotsForUpgrade(item);
}

/*
 ========================
 BG_InventoryContainsUpgrade

 Does the player hold an upgrade?
 ========================
 */
qboolean
BG_InventoryContainsUpgrade(int item, int stats[])
{
  return (stats[STAT_ITEMS] & (1 << item));
}

/*
 ========================
 BG_ActivateUpgrade

 Activates an upgrade
 ========================
 */
void
BG_ActivateUpgrade(int item, int stats[])
{
  stats[STAT_ACTIVEITEMS] |= (1 << item);
}

/*
 ========================
 BG_DeactivateUpgrade

 Deactivates an upgrade
 ========================
 */
void
BG_DeactivateUpgrade(int item, int stats[])
{
  stats[STAT_ACTIVEITEMS] &= ~(1 << item);
}

/*
 ========================
 BG_UpgradeIsActive

 Is this upgrade active?
 ========================
 */
qboolean
BG_UpgradeIsActive(int item, int stats[])
{
  return (stats[STAT_ACTIVEITEMS] & (1 << item));
}

/*
 ===============
 BG_RotateAxis

 Shared axis rotation function
 ===============
 */
qboolean
BG_RotateAxis(vec3_t surfNormal, vec3_t inAxis[3], vec3_t outAxis[3], qboolean inverse,
  qboolean ceiling)
{
  vec3_t refNormal =
  { 0.0f, 0.0f, 1.0f };
  vec3_t ceilingNormal =
  { 0.0f, 0.0f, -1.0f };
  vec3_t localNormal, xNormal;
  float rotAngle;

  //the grapplePoint being a surfNormal rotation Normal hack... see above :)
  if (ceiling)
  {
    VectorCopy(ceilingNormal, localNormal);
    VectorCopy(surfNormal, xNormal);
  }
  else
  {
    //cross the reference normal and the surface normal to get the rotation axis
    VectorCopy(surfNormal, localNormal);
    CrossProduct(localNormal, refNormal, xNormal);
    VectorNormalize(xNormal);
  }

  //can't rotate with no rotation vector
  if (VectorLength(xNormal) != 0.0f)
  {
    rotAngle = RAD2DEG(acos(DotProduct(localNormal, refNormal)));

    if (inverse)
      rotAngle = -rotAngle;

    AngleNormalize180(rotAngle);

    //hmmm could get away with only one rotation and some clever stuff later... but i'm lazy
    RotatePointAroundVector(outAxis[0], xNormal, inAxis[0], -rotAngle);
    RotatePointAroundVector(outAxis[1], xNormal, inAxis[1], -rotAngle);
    RotatePointAroundVector(outAxis[2], xNormal, inAxis[2], -rotAngle);
  }
  else
    return qfalse;

  return qtrue;
}

/*
 ===============
 BG_PositionBuildableRelativeToPlayer

 Find a place to build a buildable
 ===============
 */
void
BG_PositionBuildableRelativeToPlayer(const playerState_t *ps, const vec3_t mins, const vec3_t maxs,
  void
  (*trace)(trace_t *, const vec3_t, const vec3_t, const vec3_t, const vec3_t, int, int),
  vec3_t outOrigin, vec3_t outAngles, trace_t *tr)
{
  vec3_t forward, entityOrigin, targetOrigin;
  vec3_t angles, playerOrigin, playerNormal;
  float buildDist;

  if (ps->stats[STAT_STATE] & SS_WALLCLIMBING)
  {
    if (ps->stats[STAT_STATE] & SS_WALLCLIMBINGCEILING)
      VectorSet(playerNormal, 0.0f, 0.0f, -1.0f);
else      VectorCopy(ps->grapplePoint, playerNormal);
    }
    else
    VectorSet(playerNormal, 0.0f, 0.0f, 1.0f);

    VectorCopy(ps->viewangles, angles);
    VectorCopy(ps->origin, playerOrigin);
    buildDist = BG_FindBuildDistForClass(ps->stats[ STAT_PCLASS ]);

    AngleVectors(angles, forward, NULL, NULL);
    ProjectPointOnPlane(forward, forward, playerNormal);
    VectorNormalize(forward);

    VectorMA(playerOrigin, buildDist, forward, entityOrigin);

    VectorCopy(entityOrigin, targetOrigin);

    //so buildings can be placed facing slopes
    VectorMA(entityOrigin, 32, playerNormal, entityOrigin);

    //so buildings drop to floor
    VectorMA(targetOrigin, -128, playerNormal, targetOrigin);

    (*trace)(tr, entityOrigin, mins, maxs, targetOrigin, ps->clientNum, MASK_PLAYERSOLID);
    VectorCopy(tr->endpos, entityOrigin);
    VectorMA(entityOrigin, 0.1f, playerNormal, outOrigin);
    vectoangles(forward, outAngles);
  }

  /*
   ===============
   BG_GetValueOfEquipment

   Returns the equipment value of some human player's gear
   ===============
   */
int
BG_GetValueOfEquipment(playerState_t *ps)
{
  int i, worth = 0;

  for(i = UP_NONE + 1;i < UP_NUM_UPGRADES;i++)
  {
    if (BG_InventoryContainsUpgrade(i, ps->stats))
      worth += BG_FindPriceForUpgrade(i);
  }

  for(i = WP_NONE + 1;i < WP_NUM_WEAPONS;i++)
  {
    if (BG_InventoryContainsWeapon(i, ps->stats))
      worth += BG_FindPriceForWeapon(i);
  }

  return worth;
}

/*
 ===============
 BG_GetValueOfHuman

 Returns the kills value of some human player
 ===============
 */
int
BG_GetValueOfHuman(playerState_t *ps)
{
  float portion = BG_GetValueOfEquipment(ps) / (float) HUMAN_MAXED;

  if (portion < 0.01f)
    portion = 0.01f;
  else if (portion > 1.0f)
    portion = 1.0f;

  return ceil(ALIEN_MAX_SINGLE_KILLS * portion);
}

/*
 ===============
 atof_neg

 atof with an allowance for negative values
 ===============
 */
float
atof_neg(char *token, qboolean allowNegative)
{
  float value;

  value = atof(token);

  if (!allowNegative && value < 0.0f)
    value = 1.0f;

  return value;
}

/*
 ===============
 atoi_neg

 atoi with an allowance for negative values
 ===============
 */
int
atoi_neg(char *token, qboolean allowNegative)
{
  int value;

  value = atoi(token);

  if (!allowNegative && value < 0)
    value = 1;

  return value;
}

/*
 ===============
 BG_ParseCSVEquipmentList
 ===============
 */
void
BG_ParseCSVEquipmentList(const char *string, weapon_t *weapons, int weaponsSize,
  upgrade_t *upgrades, int upgradesSize)
{
  char buffer[MAX_STRING_CHARS];
  int i = 0, j = 0;
  char *p, *q;
  qboolean EOS = qfalse;

  Q_strncpyz(buffer, string, MAX_STRING_CHARS);

  p = q = buffer;

  while(*p != '\0')
  {
    //skip to first , or EOS
    while(*p != ',' && *p != '\0')
      p++;

    if (*p == '\0')
      EOS = qtrue;

    *p = '\0';

    //strip leading whitespace
    while(*q == ' ')
      q++;

    if (weaponsSize)
      weapons[i] = BG_FindWeaponNumForName(q);

    if (upgradesSize)
      upgrades[j] = BG_FindUpgradeNumForName(q);

    if (weaponsSize && weapons[i] == WP_NONE && upgradesSize && upgrades[j] == UP_NONE)
      Com_Printf(S_COLOR_YELLOW "WARNING: unknown equipment %s\n", q);
    else if (weaponsSize && weapons[i] != WP_NONE)
      i++;
    else if (upgradesSize && upgrades[j] != UP_NONE)
      j++;

    if (!EOS)
    {
      p++;
      q = p;
    }
    else
      break;

    if (i == (weaponsSize - 1) || j == (upgradesSize - 1))
      break;
  }

  if (weaponsSize)
    weapons[i] = WP_NONE;

  if (upgradesSize)
    upgrades[j] = UP_NONE;
}

/*
 ===============
 BG_ParseCSVClassList
 ===============
 */
void
BG_ParseCSVClassList(const char *string, pClass_t *classes, int classesSize)
{
  char buffer[MAX_STRING_CHARS];
  int i = 0;
  char *p, *q;
  qboolean EOS = qfalse;

  Q_strncpyz(buffer, string, MAX_STRING_CHARS);

  p = q = buffer;

  while(*p != '\0')
  {
    //skip to first , or EOS
    while(*p != ',' && *p != '\0')
      p++;

    if (*p == '\0')
      EOS = qtrue;

    *p = '\0';

    //strip leading whitespace
    while(*q == ' ')
      q++;

    classes[i] = BG_FindClassNumForName(q);

    if (classes[i] == PCL_NONE)
      Com_Printf(S_COLOR_YELLOW "WARNING: unknown class %s\n", q);
    else
      i++;

    if (!EOS)
    {
      p++;
      q = p;
    }
    else
      break;
  }

  classes[i] = PCL_NONE;
}

/*
 ===============
 BG_ParseCSVBuildableList
 ===============
 */
void
BG_ParseCSVBuildableList(const char *string, buildable_t *buildables, int buildablesSize)
{
  char buffer[MAX_STRING_CHARS];
  int i = 0;
  char *p, *q;
  qboolean EOS = qfalse;

  Q_strncpyz(buffer, string, MAX_STRING_CHARS);

  p = q = buffer;

  while(*p != '\0')
  {
    //skip to first , or EOS
    while(*p != ',' && *p != '\0')
      p++;

    if (*p == '\0')
      EOS = qtrue;

    *p = '\0';

    //strip leading whitespace
    while(*q == ' ')
      q++;

    buildables[i] = BG_FindBuildNumForName(q);

    if (buildables[i] == BA_NONE)
      Com_Printf(S_COLOR_YELLOW "WARNING: unknown buildable %s\n", q);
    else
      i++;

    if (!EOS)
    {
      p++;
      q = p;
    }
    else
      break;
  }

  buildables[i] = BA_NONE;
}

/*
 ============
 BG_UpgradeClassAvailable
 ============
 */
qboolean
BG_UpgradeClassAvailable(playerState_t *ps)
{
  int i;
  char buffer[MAX_STRING_CHARS];
  stage_t currentStage;

  trap_Cvar_VariableStringBuffer("g_alienStage", buffer, MAX_STRING_CHARS);
  currentStage = atoi(buffer);

  for(i = PCL_NONE + 1;i < PCL_NUM_CLASSES;i++)
  {
    if (BG_ClassCanEvolveFromTo(ps->stats[STAT_PCLASS], i, ps->persistant[PERS_CREDIT], 0) >= 0
        && BG_FindStagesForClass(i, currentStage) && BG_ClassIsAllowed(i))
    {
      return qtrue;
    }
  }

  return qfalse;
}

typedef struct gameElements_s
{
  buildable_t buildables[BA_NUM_BUILDABLES];
  pClass_t classes[PCL_NUM_CLASSES];
  weapon_t weapons[WP_NUM_WEAPONS];
  upgrade_t upgrades[UP_NUM_UPGRADES];
} gameElements_t;

static gameElements_t bg_disabledGameElements;

/*
 ============
 BG_InitAllowedGameElements
 ============
 */
void
BG_InitAllowedGameElements(void)
{
  char cvar[MAX_CVAR_VALUE_STRING];

  trap_Cvar_VariableStringBuffer("g_disabledEquipment", cvar, MAX_CVAR_VALUE_STRING);

  BG_ParseCSVEquipmentList(
    cvar, bg_disabledGameElements.weapons, WP_NUM_WEAPONS, bg_disabledGameElements.upgrades,
    UP_NUM_UPGRADES);

  trap_Cvar_VariableStringBuffer("g_disabledClasses", cvar, MAX_CVAR_VALUE_STRING);

  BG_ParseCSVClassList(cvar, bg_disabledGameElements.classes, PCL_NUM_CLASSES);

  trap_Cvar_VariableStringBuffer("g_disabledBuildables", cvar, MAX_CVAR_VALUE_STRING);

  BG_ParseCSVBuildableList(cvar, bg_disabledGameElements.buildables, BA_NUM_BUILDABLES);
}

/*
 ============
 BG_WeaponIsAllowed
 ============
 */
qboolean
BG_WeaponIsAllowed(weapon_t weapon)
{
  int i;

  for(i = 0;i < WP_NUM_WEAPONS && bg_disabledGameElements.weapons[i] != WP_NONE;i++)
  {
    if (bg_disabledGameElements.weapons[i] == weapon)
      return qfalse;
  }

  return qtrue;
}

/*
 ============
 BG_UpgradeIsAllowed
 ============
 */
qboolean
BG_UpgradeIsAllowed(upgrade_t upgrade)
{
  int i;

  for(i = 0;i < UP_NUM_UPGRADES && bg_disabledGameElements.upgrades[i] != UP_NONE;i++)
  {
    if (bg_disabledGameElements.upgrades[i] == upgrade)
      return qfalse;
  }

  return qtrue;
}

/*
 ============
 BG_ClassIsAllowed
 ============
 */
qboolean
BG_ClassIsAllowed(pClass_t class)
{
  int i;

  for(i = 0;i < PCL_NUM_CLASSES && bg_disabledGameElements.classes[i] != PCL_NONE;i++)
  {
    if (bg_disabledGameElements.classes[i] == class)
      return qfalse;
  }

  return qtrue;
}

/*
 ============
 BG_BuildableIsAllowed
 ============
 */
qboolean
BG_BuildableIsAllowed(buildable_t buildable)
{
  int i;

  for(i = 0;i < BA_NUM_BUILDABLES && bg_disabledGameElements.buildables[i] != BA_NONE;i++)
  {
    if (bg_disabledGameElements.buildables[i] == buildable)
      return qfalse;
  }

  return qtrue;
}

/*
 ============
 BG_ClientListTest
 ============
 */
qboolean
BG_ClientListTest(clientList_t *list, int clientNum)
{
  if (clientNum < 0 || clientNum >= MAX_CLIENTS || !list)
    return qfalse;
  if (clientNum < 32)
    return ((list->lo & (1 << clientNum)) != 0);
  else
    return ((list->hi & (1 << (clientNum - 32))) != 0);
}

/*
 ============
 BG_ClientListAdd
 ============
 */
void
BG_ClientListAdd(clientList_t *list, int clientNum)
{
  if (clientNum < 0 || clientNum >= MAX_CLIENTS || !list)
    return;
  if (clientNum < 32)
    list->lo |= (1 << clientNum);
  else
    list->hi |= (1 << (clientNum - 32));
}

/*
 ============
 BG_ClientListRemove
 ============
 */
void
BG_ClientListRemove(clientList_t *list, int clientNum)
{
  if (clientNum < 0 || clientNum >= MAX_CLIENTS || !list)
    return;
  if (clientNum < 32)
    list->lo &= ~(1 << clientNum);
  else
    list->hi &= ~(1 << (clientNum - 32));
}

/*
 ============
 BG_ClientListString
 ============
 */
char *
BG_ClientListString(clientList_t *list)
{
  static char s[17];

  s[0] = '\0';
  if (!list)
    return s;
  Com_sprintf(s, sizeof(s), "%08x%08x", list->hi, list->lo);
  return s;
}

/*
 ============
 BG_ClientListParse
 ============
 */
void
BG_ClientListParse(clientList_t *list, const char *s)
{
  if (!list)
    return;
  list->lo = 0;
  list->hi = 0;
  if (!s)
    return;
  if (strlen(s) != 16)
    return;
  sscanf(s, "%x%x", &list->hi, &list->lo);
}

